pax_global_header00006660000000000000000000000064147213736750014531gustar00rootroot0000000000000052 comment=3cd4290a661cb63afd112698093c3f23fecfcbb4 taskipy-1.14.1/000077500000000000000000000000001472137367500133015ustar00rootroot00000000000000taskipy-1.14.1/.github/000077500000000000000000000000001472137367500146415ustar00rootroot00000000000000taskipy-1.14.1/.github/workflows/000077500000000000000000000000001472137367500166765ustar00rootroot00000000000000taskipy-1.14.1/.github/workflows/ci.yml000066400000000000000000000024211472137367500200130ustar00rootroot00000000000000# This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: Taskipy Test CI on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: strategy: matrix: python-version: - 3.7 - 3.8 - 3.9 - '3.10' - 3.11 - 3.12 include: - python-version: 3.6 os: ubuntu-20.04 runs-on: ${{ matrix.os || 'ubuntu-latest' }} steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install Poetry run: | python -m pip install --upgrade pip pip install poetry - name: Python 3.6 Compatibility if: ${{ matrix.python-version == '3.6' }} run: | echo 'Since the old Python 3.6 CI environment uses an outdated version of poetry, remove the lockfile as it cannot be used' rm poetry.lock - name: Install dependencies run: poetry install - name: Test run: poetry run task test taskipy-1.14.1/.github/workflows/release.yml000066400000000000000000000010461472137367500210420ustar00rootroot00000000000000name: Release Python Package on: release: types: - published jobs: release: runs-on: ubuntu-22.04 environment: Release steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.12' - name: Install dependencies run: pip install setuptools==68.2.2 wheel==0.41.3 poetry==1.7.1 - name: Build and publish run: | poetry build poetry publish env: POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} taskipy-1.14.1/.github/workflows/windows.yml000066400000000000000000000012301472137367500211070ustar00rootroot00000000000000# This workflow checks that windows paths (a\b\c) work correctly when # passed as args to taskipy and that mslex is installed. name: Taskipy Windows Tests on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.9 uses: actions/setup-python@v2 with: python-version: 3.9 - name: Install Poetry run: | python -m pip install --upgrade pip pip install poetry - name: Install dependencies run: poetry install - name: Test Windows run: poetry run task test_windows taskipy-1.14.1/.gitignore000066400000000000000000000004161472137367500152720ustar00rootroot00000000000000__pycache__/ *.py[cod] *$py.class *.so .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST .mypy_cache .idea .python-version .DS_Store .venv taskipy-1.14.1/.hooks/000077500000000000000000000000001472137367500145025ustar00rootroot00000000000000taskipy-1.14.1/.hooks/make_release_commit.py000066400000000000000000000020511472137367500210370ustar00rootroot00000000000000#!/usr/bin/env python3 import tomli import secrets import subprocess import webbrowser from pathlib import Path def create_release_commit(): cwd = Path.cwd() with open(cwd / "pyproject.toml", "rb") as file: pyproject = tomli.load(file) version = pyproject["tool"]["poetry"]["version"] branch = f"release-{version}-{secrets.token_hex(12)}" p = subprocess.Popen( " && ".join( [ f"git checkout -b {branch}", "git add .", f'git commit -m "Release version {version}"', f'git tag -a "{version}" -m "Release version {version}"', f"git push --set-upstream origin {branch}", f"git push --tags", ] ), shell=True, cwd=cwd, ) p.wait() webbrowser.open( f"https://github.com/taskipy/taskipy/compare/{branch}?quick_pull=1&title=Release%20Version%20{version}&body=%3E%20Please%20describe%20your%20release%20here" ) if __name__ == "__main__": create_release_commit() taskipy-1.14.1/.pylintrc000066400000000000000000000004261472137367500151500ustar00rootroot00000000000000[MASTER] disable= C0114, R0201, W0702, C0411, R0903, R0915, W0703, W0707, R1732, R1737 [FORMAT] max-line-length=1000 no-docstring-rgx=^.* const-rgx=^.* variable-rgx=[a-z_][a-z0-9_]{0,30}$ missing-module-docstring=^.* taskipy-1.14.1/.vscode/000077500000000000000000000000001472137367500146425ustar00rootroot00000000000000taskipy-1.14.1/.vscode/.ropeproject/000077500000000000000000000000001472137367500172545ustar00rootroot00000000000000taskipy-1.14.1/.vscode/.ropeproject/config.py000066400000000000000000000112721472137367500210760ustar00rootroot00000000000000# The default ``config.py`` # flake8: noqa def set_prefs(prefs): """This function is called before opening the project""" # Specify which files and folders to ignore in the project. # Changes to ignored resources are not added to the history and # VCSs. Also they are not returned in `Project.get_files()`. # Note that ``?`` and ``*`` match all characters but slashes. # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' # '.svn': matches 'pkg/.svn' and all of its children # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject', '.hg', '.svn', '_svn', '.git', '.tox'] # Specifies which files should be considered python files. It is # useful when you have scripts inside your project. Only files # ending with ``.py`` are considered to be python files by # default. # prefs['python_files'] = ['*.py'] # Custom source folders: By default rope searches the project # for finding source folders (folders that should be searched # for finding modules). You can add paths to that list. Note # that rope guesses project source folders correctly most of the # time; use this if you have any problems. # The folders should be relative to project root and use '/' for # separating folders regardless of the platform rope is running on. # 'src/my_source_folder' for instance. # prefs.add('source_folders', 'src') # You can extend python path for looking up modules # prefs.add('python_path', '~/python/') # Should rope save object information or not. prefs['save_objectdb'] = True prefs['compress_objectdb'] = False # If `True`, rope analyzes each module when it is being saved. prefs['automatic_soa'] = True # The depth of calls to follow in static object analysis prefs['soa_followed_calls'] = 0 # If `False` when running modules or unit tests "dynamic object # analysis" is turned off. This makes them much faster. prefs['perform_doa'] = True # Rope can check the validity of its object DB when running. prefs['validate_objectdb'] = True # How many undos to hold? prefs['max_history_items'] = 32 # Shows whether to save history across sessions. prefs['save_history'] = True prefs['compress_history'] = False # Set the number spaces used for indenting. According to # :PEP:`8`, it is best to use 4 spaces. Since most of rope's # unit-tests use 4 spaces it is more reliable, too. prefs['indent_size'] = 4 # Builtin and c-extension modules that are allowed to be imported # and inspected by rope. prefs['extension_modules'] = [] # Add all standard c-extensions to extension_modules list. prefs['import_dynload_stdmods'] = True # If `True` modules with syntax errors are considered to be empty. # The default value is `False`; When `False` syntax errors raise # `rope.base.exceptions.ModuleSyntaxError` exception. prefs['ignore_syntax_errors'] = False # If `True`, rope ignores unresolvable imports. Otherwise, they # appear in the importing namespace. prefs['ignore_bad_imports'] = False # If `True`, rope will insert new module imports as # `from import ` by default. prefs['prefer_module_from_imports'] = False # If `True`, rope will transform a comma list of imports into # multiple separate import statements when organizing # imports. prefs['split_imports'] = False # If `True`, rope will remove all top-level import statements and # reinsert them at the top of the module when making changes. prefs['pull_imports_to_top'] = True # If `True`, rope will sort imports alphabetically by module name instead # of alphabetically by import statement, with from imports after normal # imports. prefs['sort_imports_alphabetically'] = False # Location of implementation of # rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general # case, you don't have to change this value, unless you're an rope expert. # Change this value to inject you own implementations of interfaces # listed in module rope.base.oi.type_hinting.providers.interfaces # For example, you can add you own providers for Django Models, or disable # the search type-hinting in a class hierarchy, etc. prefs['type_hinting_factory'] = ( 'rope.base.oi.type_hinting.factory.default_type_hinting_factory') def project_opened(project): """This function is called after opening the project""" # Do whatever you like here! taskipy-1.14.1/.vscode/.ropeproject/objectdb000066400000000000000000000000061472137367500207470ustar00rootroot00000000000000}q.taskipy-1.14.1/.vscode/settings.json000066400000000000000000000002621472137367500173750ustar00rootroot00000000000000{ "editor.tabSize": 4, "python.linting.enabled": true, "python.linting.pylintEnabled": true, "python.linting.mypyEnabled": true, "python.formatting.provider": "none" } taskipy-1.14.1/LICENSE000066400000000000000000000020521472137367500143050ustar00rootroot00000000000000MIT License Copyright (c) 2019 Roy Sommer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.taskipy-1.14.1/README.md000066400000000000000000000247621472137367500145730ustar00rootroot00000000000000

- [General](#general) - [Requirements](#requirements) - [Usage](#usage) - [Installation](#installation) - [Adding Tasks](#adding-tasks) - [Running Tasks](#running-tasks) - [Passing Command Line Args to Tasks](#passing-command-line-args-to-tasks) - [Composing Tasks](#composing-tasks) - [Grouping Subtasks Together](#grouping-subtasks-together) - [Pre Task Hook](#pre-task-hook) - [Post Task Hook](#post-task-hook) - [Using Variables](#using-variables) - [String Formatting](#string-formatting) - [Always Use Variables](#always-use-variables) - [Recursive Variables](#recursive-variables) - [Working directory](#working-directory) - [Using Taskipy Without Poetry](#using-taskipy-without-poetry) - [Installing With PIP](#installing-with-pip) - [Running Tasks](#running-tasks-1) - [Advanced Use Cases](#advanced-use-cases) - [Contribution](#contribution) ## General [![pypi](https://img.shields.io/pypi/v/taskipy?style=flat-square)](https://pypi.org/project/taskipy/) [![pypi-downloads](https://img.shields.io/pypi/dm/taskipy?style=flat-square)](https://pypi.org/project/taskipy/) [![Conda Version](https://img.shields.io/conda/vn/conda-forge/taskipy.svg)](https://anaconda.org/conda-forge/taskipy) [![ci](https://img.shields.io/github/actions/workflow/status/taskipy/taskipy/ci.yml?style=flat-square)](https://github.com/taskipy/taskipy/actions/workflows/ci.yml) **The complementary task runner for python.** Every development pipeline has tasks, such as `test`, `lint` or `publish`. With taskipy, you can define those tasks in one file and run them with a simple command. For instance, instead of running the following command: ```bash python -m unittest tests/test_*.py ``` You can create a task called `test` and simply run: ```bash poetry run task test ``` Or (if you're not using poetry): ```bash task test ``` In addition, you can compose tasks and group them together, and also create dependencies between them. This project is heavily inspired by npm's [run script command](https://docs.npmjs.com/cli/run-script). ## Requirements Python 3.6 or newer. Your project directory should include a valid `pyproject.toml` file, as specified in [PEP-518](https://www.python.org/dev/peps/pep-0518/). ## Usage ### Installation To install taskipy as a dev dependency, simply run: ```bash poetry add --dev taskipy ``` For Anaconda Python-based environments, taskipy is also available via conda-forge: ```bash conda install -c conda-forge taskipy ``` ### Adding Tasks In your `pyproject.toml` file, add a new section called `[tool.taskipy.tasks]`. The section is a key-value map, from the names of the task to the actual command that should be run in the shell. There are two ways to define tasks. The easy way is to simply write the command down as a string: __pyproject.toml__ ```toml [tool.taskipy.tasks] test = "python -m unittest tests/test_*.py" lint = "pylint tests taskipy" ``` Alternatively, you can define tasks more explicitly by declaring both the command and a helpful description using an inline table: __pyproject.toml__ ```toml [tool.taskipy.tasks] test = { cmd = "python -m unittest tests/test_*.py", help = "runs all unit tests" } lint = { cmd = "pylint tests taskipy", help = "confirms code style using pylint" } ``` The explicit notation is more verbose, but provides better context to anyone who uses the task. ### Running Tasks In order to run a task, run the following command in your terminal: ```bash $ poetry run task test ``` You can also list all existing tasks by running the following: ```bash $ poetry run task --list test python -m unittest tests/test_*.py lint pylint tests taskipy ``` If you declared your task explicitly, you will see the description of the task by the side of the task's name: ```bash $ poetry run task --list test runs all unit tests lint confirms code style using pylint ``` ### Passing Command Line Args to Tasks If you want to pass command line arguments to tasks (positional or named), simply append them to the end of the task command. For example, running the above task like this: ```bash poetry run task test -h ``` Is equivalent to running: ```bash python -m unittest tests/test_*.py -h ``` And will show unittest's help instead of actually running it. > ⚠️ Note: if you are using pre \ post hooks, do notice that arguments are not passed to them, only to the task itself. ### Composing Tasks #### Grouping Subtasks Together Some tasks are composed of multiple subtasks. Instead of writing plain shell commands and stringing them together, you can break them down into multiple subtasks: ```toml [tool.taskipy.tasks] lint_pylint = "pylint tests taskipy" lint_mypy = "mypy tests taskipy" ``` And then create a composite task: ```toml [tool.taskipy.tasks] lint = "task lint_pylint && task lint_mypy" lint_pylint = "pylint tests taskipy" lint_mypy = "mypy tests taskipy" ``` #### Pre Task Hook Tasks might also depend on one another. For example, tests might require some binaries to be built. Take the two following commands, for instance: ```toml [tool.taskipy.tasks] test = "python -m unittest tests/test_*.py" build = "make ." ``` You could make tests depend on building, by using the **pretask hook**: ```toml [tool.taskipy.tasks] pre_test = "task build" test = "python -m unittest tests/test_*.py" build = "make ." ``` The pretask hook looks for `pre_` task for a given `task_name`. It will run it before running the task itself. If the pretask fails, then taskipy will exit without running the task itself. #### Post Task Hook From time to time, you might want to run a task in conjunction with another. For example, you might want to run linting after a successful test run. Take the two following commands, for instance: ```toml [tool.taskipy.tasks] test = "python -m unittest tests/test_*.py" lint = "pylint tests taskipy" ``` You could make tests trigger linting, by using the **posttask hook**: ```toml [tool.taskipy.tasks] test = "python -m unittest tests/test_*.py" post_test = "task lint" lint = "pylint tests taskipy" ``` The posttask hook looks for `post_` task for a given `task_name`. It will run it after running the task itself. If the task failed, then taskipy will not run the posttask hook. ### Using Variables In some cases, you might find yourself passing the same arguments over and over again. Let us take a look at the following tasks: ```toml [tool.taskipy.tasks] lint = "pylint path/to/my_module" black = "black path/to/my_module" ``` As you can see, we provide the same path argument to both `pylint` and `black`. In order to encourage DRY and improve your ability to change these values later on, taskipy actually lets you declare and reuse variables in your tasks: ```toml [tool.taskipy.variables] path = "path/to/my_module" [tool.taskipy.tasks] lint = { cmd = "pylint {path}", use_vars = true } black = { cmd = "pylint {path}", use_vars = true } ``` We have made the following changes to our configuration: 1. We declared variables under `tool.taskipy.variables` 2. We flagged the relevant task using `use_vars` to note that they should use the variables 3. We replaced the repeating path with a `{path}` variable #### String Formatting The formatting of the task commands uses python's own `string.format` method, and therefore supports everything that python's [formatted string literals](https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals) let you do. #### Always Use Variables Using variables is opt-in, which means that by default commands do **not** use them, and you will have to turn them on a task to task basis. If you want to turn on `use_vars` globally, all you need to do is to declare that under taskipy's **settings** table: ```toml [tool.taskipy.settings] use_vars = true [tool.taskipy.variables] path = "path/to/my_module" [tool.taskipy.tasks] lint = "pylint {path}" black = "black {path}" ``` #### Recursive Variables If we want to use variables within other variables, we can utilize recursive variables. By default, variables are not recursive, but we can specify a variable to be recursive by setting the `recursive` key to `true`. ```toml [tool.taskipy.settings] use_vars = true [tool.taskipy.variables] src_dir = "src" package_dir = { var = "{src_dir}/package", recursive = true } [tool.taskipy.tasks] echo = "echo {package_dir}" ``` In this example, we could run `task echo` and we would then see `src/package`. ### Working directory By default, all tasks run from the directory where they are called. This makes possible to change folder and run flexible tasks depending on the current folder. However, some tasks may need to always run in the same working directory, regardless from where they are called. If you want tasks to always run relative to a specific path, you can use the `"cwd"` setting to define a current working directory of the task relative to the root of the project (where the `pyproject.toml` file is): ```toml [tool.taskipy.tasks] echo = { cmd = "python -c \"import os; print(os.getcwd())\"" , cwd = "."} ``` In this example, running `task echo` will print the directory path of the file `pyproject.toml` regardless of the folder that you call this task. If you want to define `cwd` globally (setting all tasks to always run from the same working directory) you just need to declare that under taskipy's **settings** table: ```toml [tool.taskipy.settings] cwd = "." ``` ### Using Taskipy Without Poetry Taskipy was created with poetry projects in mind, but actually only requires a valid `pyproject.toml` file in your project's directory. As a result, you can use it even without poetry: #### Installing With PIP Install taskipy on your machine or in your virtualenv using: ```bash pip install taskipy ``` #### Running Tasks Head into your project's directory (don't forget to activate virtualenv if you're using one), and run the following command: ```bash task TASK ``` Where `TASK` is the name of your task. ### Advanced Use Cases If you have a more specific use case, you might not be the first to run into it! Head over to the [ADVANCED_FEATURES](./docs/ADVANCED_FEATURES.md) doc, and look it up. ## Contribution All kinds of contributions are welcome! Feel free to request features and report bugs via issues, and contribute your own beautiful code via pull requests! The taskipy project is maintained by [Roy Sommer](https://github.com/illBeRoy). If you're interested in joining the team, feel free to let me know! taskipy-1.14.1/docs/000077500000000000000000000000001472137367500142315ustar00rootroot00000000000000taskipy-1.14.1/docs/ADVANCED_FEATURES.md000066400000000000000000000037351472137367500172260ustar00rootroot00000000000000# Advanced Use Cases This is a list of quality of life features that might be useful to you if you have a specific use case that you want to easily solve. You can go over the use cases list and try to find the specific use case which you're trying to solve. If you cannot find your specific use case, feel free to open an issue or a PR! ## Use Cases For each of the use cases below, click on the arrow in order to get to the relevant feature. 1. I want to load environment variables before every task ([⏩](#custom-runners)) 2. I want to run all tasks in a specific virtualenv ([⏩](#custom-runners)) 3. I want to run all tasks in a specific shell \ ssh ([⏩](#custom-runners)) ## Features ### Custom Runners #### Requirement Under some circumstances, we would like to run all of our tasks in a consistent context; e.g., in a specific shell, a remote shell with `ssh`, some custom `virtualenv` wrapper, or even `dotenv`. Currently, we can configure this on a task to task basis, e.g.: ```toml [tool.taskipy.tasks] test = "dotenv run unittest ." ``` But if we want all of our tasks to use the same runner (such as `dotenv` in the above example) we would have to manually write all of them as such: ```toml [tool.taskipy.tasks] test = "dotenv run unittest ." lint = "dotenv run pylint taskipy" release = "dotenv run release-it" start = "dotenv run python ." ... ``` And so on. Such cases are where we need a global wrapper, so we wouldn't have to repeat the prefix with every command. #### Solution The `tool.taskipy.settings.runner` option: when configured, adds the given runner command as a prefix to every task we run. Let's take a look at the following example, where we want to load environment variables before every task: ```toml [tool.taskipy.settings] runner = "dotenv run" [tool.taskipy.tasks] test = "unittest ." lint = "pylint taskipy" ``` Now, running `task test` will actually run: ```bash dotenv run unittest . ``` Which means that we implicitly initialize the env before every task. taskipy-1.14.1/logo.svg000066400000000000000000000037731472137367500147740ustar00rootroot00000000000000 taskipy-1.14.1/poetry.lock000066400000000000000000001157071472137367500155100ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "astroid" version = "2.9.0" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = "~=3.6" files = [ {file = "astroid-2.9.0-py3-none-any.whl", hash = "sha256:776ca0b748b4ad69c00bfe0fff38fa2d21c338e12c84aa9715ee0d473c422778"}, {file = "astroid-2.9.0.tar.gz", hash = "sha256:5939cf55de24b92bda00345d4d0659d01b3c7dafb5055165c330bc7c568ba273"}, ] [package.dependencies] lazy-object-proxy = ">=1.4.0" setuptools = ">=20.0" typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} wrapt = ">=1.11,<1.14" [[package]] name = "astroid" version = "3.2.2" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, ] [[package]] name = "colorama" version = "0.4.5" description = "Cross-platform colored terminal text." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] [[package]] name = "dill" version = "0.3.8" description = "serialize all of Python" optional = false python-versions = ">=3.8" files = [ {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "isort" version = "5.8.0" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.6,<4.0" files = [ {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, ] [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] pipfile-deprecated-finder = ["pipreqs", "requirementslib"] requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] name = "isort" version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] colors = ["colorama (>=0.4.6)"] [[package]] name = "lazy-object-proxy" version = "1.7.1" description = "A fast and thorough lazy object proxy." optional = false python-versions = ">=3.6" files = [ {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, ] [[package]] name = "mccabe" version = "0.6.1" description = "McCabe checker, plugin for flake8" optional = false python-versions = "*" files = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] [[package]] name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] [[package]] name = "mslex" version = "1.2.0" description = "shlex for windows" optional = false python-versions = ">=3.5" files = [ {file = "mslex-1.2.0-py3-none-any.whl", hash = "sha256:c68ec637485ee3544c5847c1b4e78b02940b32708568fb1d8715491815aa2341"}, {file = "mslex-1.2.0.tar.gz", hash = "sha256:79e2abc5a129dd71cdde58a22a2039abb7fa8afcbac498b723ba6e9b9fbacc14"}, ] [[package]] name = "mypy" version = "0.971" description = "Optional static typing for Python" optional = false python-versions = ">=3.6" files = [ {file = "mypy-0.971-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"}, {file = "mypy-0.971-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5"}, {file = "mypy-0.971-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3"}, {file = "mypy-0.971-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655"}, {file = "mypy-0.971-cp310-cp310-win_amd64.whl", hash = "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103"}, {file = "mypy-0.971-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca"}, {file = "mypy-0.971-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417"}, {file = "mypy-0.971-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09"}, {file = "mypy-0.971-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8"}, {file = "mypy-0.971-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0"}, {file = "mypy-0.971-cp37-cp37m-win_amd64.whl", hash = "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2"}, {file = "mypy-0.971-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27"}, {file = "mypy-0.971-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856"}, {file = "mypy-0.971-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71"}, {file = "mypy-0.971-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27"}, {file = "mypy-0.971-cp38-cp38-win_amd64.whl", hash = "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58"}, {file = "mypy-0.971-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6"}, {file = "mypy-0.971-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe"}, {file = "mypy-0.971-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9"}, {file = "mypy-0.971-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf"}, {file = "mypy-0.971-cp39-cp39-win_amd64.whl", hash = "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0"}, {file = "mypy-0.971-py3-none-any.whl", hash = "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9"}, {file = "mypy-0.971.tar.gz", hash = "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56"}, ] [package.dependencies] mypy-extensions = ">=0.4.3" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} typing-extensions = ">=3.10" [package.extras] dmypy = ["psutil (>=4.0)"] python2 = ["typed-ast (>=1.4.0,<2)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "parameterized" version = "0.8.1" description = "Parameterized testing with any Python test framework" optional = false python-versions = "*" files = [ {file = "parameterized-0.8.1-py2.py3-none-any.whl", hash = "sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9"}, {file = "parameterized-0.8.1.tar.gz", hash = "sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c"}, ] [package.extras] dev = ["jinja2"] [[package]] name = "platformdirs" version = "2.4.0" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.6" files = [ {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, ] [package.extras] docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] [[package]] name = "platformdirs" version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] type = ["mypy (>=1.8)"] [[package]] name = "psutil" version = "6.1.0" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"}, {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"}, {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"}, {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"}, {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"}, {file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"}, {file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"}, {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"}, {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"}, {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"}, {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"}, {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"}, {file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"}, {file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"}, {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"}, {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"}, {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"}, ] [package.extras] dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"] test = ["pytest", "pytest-xdist", "setuptools"] [[package]] name = "pylint" version = "2.12.0" description = "python code static checker" optional = false python-versions = "~=3.6" files = [ {file = "pylint-2.12.0-py3-none-any.whl", hash = "sha256:ba00afcb1550bc217bbcb0eb76c10cb8335f7417a3323bdd980c29fb5b59f8d2"}, {file = "pylint-2.12.0.tar.gz", hash = "sha256:245c87e5da54c35b623c21b35debf87d93b18bf9e0229515cc172d0b83d627cd"}, ] [package.dependencies] astroid = ">=2.9.0,<2.10" colorama = {version = "*", markers = "sys_platform == \"win32\""} isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" platformdirs = ">=2.2.0" toml = ">=0.9.2" typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [[package]] name = "pylint" version = "3.2.3" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ {file = "pylint-3.2.3-py3-none-any.whl", hash = "sha256:b3d7d2708a3e04b4679e02d99e72329a8b7ee8afb8d04110682278781f889fa8"}, {file = "pylint-3.2.3.tar.gz", hash = "sha256:02f6c562b215582386068d52a30f520d84fdbcf2a95fc7e855b816060d048b60"}, ] [package.dependencies] astroid = ">=3.2.2,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomlkit = ">=0.10.1" [package.extras] spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] [[package]] name = "rope" version = "0.14.0" description = "a python refactoring library..." optional = false python-versions = "*" files = [ {file = "rope-0.14.0-py2-none-any.whl", hash = "sha256:6b728fdc3e98a83446c27a91fc5d56808a004f8beab7a31ab1d7224cecc7d969"}, {file = "rope-0.14.0-py3-none-any.whl", hash = "sha256:f0dcf719b63200d492b85535ebe5ea9b29e0d0b8aebeb87fe03fc1a65924fdaf"}, {file = "rope-0.14.0.tar.gz", hash = "sha256:c5c5a6a87f7b1a2095fb311135e2a3d1f194f5ecb96900fdd0a9100881f48aaf"}, ] [[package]] name = "setuptools" version = "59.6.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.6" files = [ {file = "setuptools-59.6.0-py3-none-any.whl", hash = "sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"}, {file = "setuptools-59.6.0.tar.gz", hash = "sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=8.2)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-inline-tabs", "sphinxcontrib-towncrier"] testing = ["flake8-2020", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "paver", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy", "pytest-virtualenv (>=1.2.7)", "pytest-xdist", "sphinx", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] [[package]] name = "tomli" version = "1.2.3" description = "A lil' TOML parser" optional = false python-versions = ">=3.6" files = [ {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, ] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "tomlkit" version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] name = "typed-ast" version = "1.5.5" description = "a fork of Python 2 and 3 ast modules with type comment support" optional = false python-versions = ">=3.6" files = [ {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, ] [[package]] name = "typing-extensions" version = "4.1.1" description = "Backported and Experimental Type Hints for Python 3.6+" optional = false python-versions = ">=3.6" files = [ {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, ] [[package]] name = "wrapt" version = "1.13.3" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"}, {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"}, {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"}, {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"}, {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"}, {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"}, {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"}, {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"}, {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"}, {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"}, {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"}, {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"}, {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"}, {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"}, {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"}, {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"}, {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"}, {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"}, {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"}, {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"}, {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"}, {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"}, {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"}, {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"}, {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"}, {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"}, {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"}, {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"}, {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"}, {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"}, {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"}, {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"}, {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"}, {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"}, {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"}, {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"}, {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"}, {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"}, {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"}, {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"}, {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"}, {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"}, {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"}, {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"}, {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"}, {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"}, {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"}, ] [metadata] lock-version = "2.0" python-versions = "^3.6" content-hash = "dc69616fd7766ddb7741dd4fc803dc65e05f412fe5f9c3799b275b82a2be40e9" taskipy-1.14.1/pyproject.toml000066400000000000000000000040111472137367500162110ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" version = "1.14.1" authors = ["Roy Sommer "] readme = "README.md" repository = "https://github.com/taskipy/taskipy" license = "MIT" keywords = ["tasks", "task runner", "development"] classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Topic :: Software Development :: Build Tools", ] packages = [{ include = "taskipy" }] [tool.poetry.scripts] task = 'taskipy.cli:main' [tool.poetry.dependencies] python = "^3.6" tomli = [ { version = "^2.0.1", python = "^3.7" }, { version = "^1.2.3", python = "~3.6" }, ] psutil = ">=5.7.2,<7" mslex = { version = "^1.1.0", markers = "sys_platform == 'win32'" } colorama = "^0.4.4" [tool.poetry.dev-dependencies] pylint = [ { version = "2.12.0", python = ">=3.6,<3.11" }, { version = "^3.0.0", python = "^3.11" }, ] mypy = "0.971" rope = "0.14.0" parameterized = "0.8.1" [tool.taskipy.tasks] test = { cmd = "python -m unittest -v tests/test_*.py", help = "runs all tests" } post_test = "./task lint" test_windows = { cmd = "python -m unittest -v tests\\test_windows.py", help = "this is a command that is run by our Windows CI on Github" } lint = { cmd = "./task lint_pylint && ./task lint_mypy", help = "lint project with pylint for style and mypy for typings" } lint_pylint = "pylint tests taskipy" lint_mypy = "mypy tests taskipy" make_release_commit = { cmd = "python ./.hooks/make_release_commit.py", help = "creates a tagged commit for the release. do not use directly" } pre_publish_patch = "./task test" publish_patch = { cmd = "poetry version patch && ./task make_release_commit", help = "publishes a patch version, that has only fixes but no new features (x.x.PATCH)" } pre_publish_minor = "./task test" publish_minor = { cmd = "poetry version minor && ./task make_release_commit", help = "publishes a minor version, which has new features (x.MINOR.x)" } [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api" taskipy-1.14.1/task000077700000000000000000000000001472137367500167662taskipy/cli.pyustar00rootroot00000000000000taskipy-1.14.1/task.bat000066400000000000000000000000371472137367500147330ustar00rootroot00000000000000python %~dp0taskipy\cli.py %* taskipy-1.14.1/taskipy/000077500000000000000000000000001472137367500147655ustar00rootroot00000000000000taskipy-1.14.1/taskipy/__init__.py000066400000000000000000000000001472137367500170640ustar00rootroot00000000000000taskipy-1.14.1/taskipy/cli.py000077500000000000000000000031421472137367500161110ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import sys from pathlib import Path from typing import List, Union from taskipy.exceptions import TaskipyError, InvalidUsageError from taskipy.task_runner import TaskRunner def main(): exit_code = run(sys.argv[1:]) sys.exit(exit_code) def run(args: List[str], cwd: Union[str, Path, None] = None) -> int: """Run the taskipy CLI programmatically. Args: args: The arguments passed to the taskipy CLI. cwd: The working directory to run the task in. If not provided, defaults to the current working directory. Returns: 0 on success; > 0 for an error. """ parser = argparse.ArgumentParser( prog='task', description='runs a task specified in your pyproject.toml under [tool.taskipy.tasks]', ) parser.add_argument('-l', '--list', help='show list of available tasks', action='store_true') parser.add_argument('name', help='name of the task', nargs='?') parser.add_argument('args', nargs=argparse.REMAINDER, help='arguments to pass to the task') parsed_args = parser.parse_args(args=args) try: cwd = Path(cwd).resolve() if cwd is not None else Path.cwd() runner = TaskRunner(cwd) if parsed_args.list: runner.list() return 0 if parsed_args.name is None: raise InvalidUsageError(parser) return runner.run(parsed_args.name, parsed_args.args) except TaskipyError as e: print(e) return e.exit_code except Exception as e: print(e) return 1 if __name__ == '__main__': main() taskipy-1.14.1/taskipy/exceptions.py000066400000000000000000000057201472137367500175240ustar00rootroot00000000000000from argparse import ArgumentParser from typing import Optional class TaskipyError(Exception): exit_code = 1 class InvalidRunnerTypeError(TaskipyError): def __str__(self): return ( 'invalid value: runner is not a string. ' 'please check [tool.taskipy.settings.runner]' ) class MissingPyProjectFileError(TaskipyError): def __str__(self): return 'no pyproject.toml file found in this directory or parent directories' class MalformedPyProjectError(TaskipyError): def __init__(self, reason: Optional[str] = None): super().__init__() self.reason = reason def __str__(self): message = 'pyproject.toml file is malformed and could not be read' if self.reason: message += f'. reason: {self.reason}' return message class TaskNotFoundError(TaskipyError): exit_code = 127 def __init__(self, task_name: str, suggestion: Optional[str] = None): super().__init__() self.task = task_name self.suggestion = suggestion def __str__(self): if self.suggestion: return f'could not find task "{self.task}", did you mean "{self.suggestion}"?' return f'could not find task "{self.task}"' class MalformedTaskError(TaskipyError): def __init__(self, task_name: str, reason: str): super().__init__() self.task = task_name self.reason = reason def __str__(self): return f'the task "{self.task}" in the pyproject.toml file is malformed. reason: {self.reason}' class InvalidUsageError(TaskipyError): exit_code = 127 def __init__(self, parser: ArgumentParser): super().__init__() self.__parser = parser def __str__(self): return self.__parser.format_usage().strip('\n') class MissingTaskipySettingsSectionError(TaskipyError): exit_code = 127 def __str__(self): return ( 'no settings found. add a [tools.taskipy.settings]' 'section to your pyproject.toml' ) class MissingTaskipyTasksSectionError(TaskipyError): exit_code = 127 def __str__(self): return ( 'no tasks found. add a [tool.taskipy.tasks] ' 'section to your pyproject.toml' ) class EmptyTasksSectionError(TaskipyError): exit_code = 127 def __str__(self): return ( 'no tasks found. create your first task ' 'by adding it to your pyproject.toml file under [tool.taskipy.tasks]' ) class CircularVariableError(TaskipyError): exit_code = 127 def __str__(self): return 'cannot resolve variables, found variables that depend on each other.' class InvalidVariableError(TaskipyError): exit_code = 127 def __init__(self, variable: str, reason: str) -> None: super().__init__() self.variable = variable self.reason = reason def __str__(self): return f'variable {self.variable} is invalid. reason: {self.reason}' taskipy-1.14.1/taskipy/list.py000066400000000000000000000025601472137367500163150ustar00rootroot00000000000000import shutil import textwrap import colorama # type: ignore from typing import Iterable, Optional from taskipy.task import Task from taskipy.exceptions import EmptyTasksSectionError class TasksListFormatter: def __init__(self, tasks: Iterable[Task]): if not tasks: raise EmptyTasksSectionError() self.__tasks = tasks colorama.init() def print(self, line_width: Optional[int] = None): if not line_width: line_width = shutil.get_terminal_size().columns tasks_col = [task.name for task in self.__tasks] longest_item_in_tasks_col = len(max(tasks_col, key=len)) desc_col_wrap_indent = ' ' * (longest_item_in_tasks_col + 1) desc_col_width = line_width - len(desc_col_wrap_indent) for task in self.__tasks: name_text = task.name desc_text = task.description or task.command tasks_col_text = f'{name_text:<{longest_item_in_tasks_col}}' desc_col_text = '\n'.join(textwrap.wrap(desc_text, width=desc_col_width, subsequent_indent=desc_col_wrap_indent)) print(f'{self.__highlight(tasks_col_text)} {desc_col_text}') def __highlight(self, text: str): return f'{colorama.Fore.CYAN}{text}{colorama.Style.RESET_ALL}' taskipy-1.14.1/taskipy/py.typed000066400000000000000000000000001472137367500164520ustar00rootroot00000000000000taskipy-1.14.1/taskipy/pyproject.py000066400000000000000000000066071472137367500173670ustar00rootroot00000000000000import tomli from pathlib import Path from typing import Any, Dict, MutableMapping, Optional, Union from taskipy.task import Task from taskipy.variable import Variable from taskipy.exceptions import ( InvalidRunnerTypeError, InvalidVariableError, MalformedPyProjectError, MissingPyProjectFileError, MissingTaskipyTasksSectionError, ) class PyProject: def __init__(self, base_dir: Path): self.__pyproject_path = PyProject.__find_pyproject_path(base_dir) self.__items = PyProject.__load_toml_file(self.__pyproject_path) @property def tasks(self) -> Dict[str, Task]: try: toml_tasks = self.__items['tool']['taskipy']['tasks'].items() except KeyError: raise MissingTaskipyTasksSectionError() tasks = {} for name, toml_contents in toml_tasks: tasks[name] = Task(name, toml_contents) return tasks @property def dirpath(self) -> Path: return self.__pyproject_path.parent @property def variables(self) -> Dict[str, Variable]: try: toml_vars = self.__items['tool']['taskipy'].get('variables', {}) except KeyError: return {} vars_dict: Dict[str, Variable] = {} for name, toml_contents in toml_vars.items(): if isinstance(toml_contents, str): vars_dict[name] = Variable(name, toml_contents, recursive=False) elif ( isinstance(toml_contents, dict) and isinstance(toml_contents.get('var'), str) ): vars_dict[name] = Variable( name, toml_contents['var'], toml_contents.get('recursive', False), ) else: raise InvalidVariableError( name, f'expected variable to contain a string or be a table ' 'with a key "var" that contains a string value, got ' f'{toml_contents}.' ) return vars_dict @property def settings(self) -> dict: try: return self.__items['tool']['taskipy']['settings'] except KeyError: return {} @property def runner(self) -> Optional[str]: try: runner = self.settings['runner'] if not isinstance(runner, str): raise InvalidRunnerTypeError() return runner.strip() except KeyError: return None @staticmethod def __load_toml_file(file_path: Union[str, Path]) -> MutableMapping[str, Any]: try: if isinstance(file_path, str): file_path = Path(file_path).resolve() with open(file_path, 'rb') as file: return tomli.load(file) except FileNotFoundError: raise MissingPyProjectFileError() except tomli.TOMLDecodeError as e: raise MalformedPyProjectError(reason=str(e)) @staticmethod def __find_pyproject_path(base_dir: Path) -> Path: def candidate_dirs(base: Path): yield base for parent in base.parents: yield parent for candidate_dir in candidate_dirs(base_dir): pyproject = candidate_dir / 'pyproject.toml' if pyproject.exists(): return pyproject raise MissingPyProjectFileError() taskipy-1.14.1/taskipy/task.py000066400000000000000000000061011472137367500162770ustar00rootroot00000000000000from typing import Optional from taskipy.exceptions import MalformedTaskError class Task: def __init__(self, task_name: str, task_toml_contents: object): self.__task_name = task_name self.__task_command = self.__extract_task_command(task_toml_contents) self.__task_description = self.__extract_task_description(task_toml_contents) self.__task_use_vars = self.__extract_task_use_vars(task_toml_contents) self.__task_workdir = self.__extract_task_workdir(task_toml_contents) @property def name(self) -> str: return self.__task_name @property def command(self) -> str: return self.__task_command @property def workdir(self) -> Optional[str]: return self.__task_workdir @property def description(self) -> str: return self.__task_description @property def use_vars(self) -> Optional[bool]: return self.__task_use_vars def __extract_task_use_vars(self, task_toml_contents: object) -> Optional[bool]: if isinstance(task_toml_contents, str): return None if isinstance(task_toml_contents, dict): value = task_toml_contents.get('use_vars') if value is not None and not isinstance(value, bool): raise MalformedTaskError(self.__task_name, f'task\'s "use_vars" arg has to be bool type got {type(value)}') return value raise MalformedTaskError(self.__task_name, 'tasks must be strings, or dicts that contain { cmd, cwd, help, use_vars }') def __extract_task_command(self, task_toml_contents: object) -> str: if isinstance(task_toml_contents, str): return task_toml_contents if isinstance(task_toml_contents, dict): try: return task_toml_contents['cmd'] except KeyError: raise MalformedTaskError(self.__task_name, 'the task item does not have the "cmd" property') raise MalformedTaskError(self.__task_name, 'tasks must be strings, or dicts that contain { cmd, cwd, help, use_vars }') def __extract_task_workdir(self, task_toml_contents: object) -> Optional[str]: if isinstance(task_toml_contents, str): return None if isinstance(task_toml_contents, dict): value = task_toml_contents.get('cwd') if value is not None and not isinstance(value, str): raise MalformedTaskError(self.__task_name, f'task\'s "cwd" arg has to be str type got {type(value)}') return value raise MalformedTaskError(self.__task_name, 'tasks must be strings, or dicts that contain { cmd, cwd, help, use_vars }') def __extract_task_description(self, task_toml_contents: object) -> str: if isinstance(task_toml_contents, str): return '' if isinstance(task_toml_contents, dict): try: return task_toml_contents['help'] except KeyError: return '' raise MalformedTaskError(self.__task_name, 'tasks must be strings, or dicts that contain { cmd, cwd, help, use_vars }') taskipy-1.14.1/taskipy/task_runner.py000066400000000000000000000161741472137367500177030ustar00rootroot00000000000000import sys import platform import signal import subprocess from difflib import get_close_matches from pathlib import Path from types import FrameType from typing import Callable, Dict, List, Tuple, Union, Optional import psutil # type: ignore from taskipy.exceptions import CircularVariableError, TaskNotFoundError, MalformedTaskError from taskipy.list import TasksListFormatter from taskipy.pyproject import PyProject from taskipy.task import Task from taskipy.variable import Variable if platform.system() == 'Windows': import mslex as shlex # type: ignore # pylint: disable=E0401 else: import shlex # type: ignore[no-redef] class TaskRunner: def __init__(self, cwd: Union[str, Path]): cwd_as_path = cwd if isinstance(cwd, Path) else Path(cwd) self.__project = PyProject(cwd_as_path) self.__working_dir = self.__get_working_dir() or cwd_as_path def list(self): """lists tasks to stdout""" formatter = TasksListFormatter(self.__project.tasks.values()) formatter.print() def run(self, task_name: str, args: List[str]) -> int: pre_command, command, post_command = self.__get_formatted_commands(task_name) self.__working_dir = self.__get_working_dir(task_name) or self.__working_dir if pre_command is not None: exit_code = self.__run_command_and_return_exit_code(pre_command) if exit_code != 0: return exit_code exit_code = self.__run_command_and_return_exit_code(command, args) if exit_code != 0: return exit_code if post_command is not None: exit_code = self.__run_command_and_return_exit_code(post_command) if exit_code != 0: return exit_code return 0 def __get_formatted_commands( self, task_name: str ) -> Tuple[Optional[str], str, Optional[str]]: pre_task, task, post_task = self.__get_tasks(task_name) should_resolve_vars = ( self.__is_using_vars([pre_task, task, post_task]) or self.__project.settings.get('use_vars') is True ) variables = self.__resolve_variables() if should_resolve_vars else {} pre_command = None if pre_task is not None: pre_command = self.__format_task_command(pre_task, variables) command = self.__format_task_command(task, variables) post_command = None if post_task is not None: post_command = self.__format_task_command(post_task, variables) return pre_command, command, post_command def __get_tasks(self, task_name: str) -> Tuple[Optional[Task], Task, Optional[Task]]: pre_task = self.__pre_task(task_name) post_task = self.__post_task(task_name) try: task = self.__project.tasks[task_name] except KeyError: suggestion = None closest_match = get_close_matches(task_name, self.__project.tasks, n=1, cutoff=0.5) if closest_match: suggestion = closest_match[0] raise TaskNotFoundError(task_name, suggestion) return pre_task, task, post_task def __pre_task(self, task_name: str) -> Optional[Task]: return self.__project.tasks.get(f'pre_{task_name}') def __post_task(self, task_name: str) -> Optional[Task]: return self.__project.tasks.get(f'post_{task_name}') def __is_using_vars(self, tasks: List[Optional[Task]]) -> bool: use_vars = False for task in tasks: if task is not None and task.use_vars: use_vars = True break return use_vars def __resolve_variables(self) -> Dict[str, str]: nonrecursive_vars, recursive_vars = self.__get_variable_types( self.__project.variables ) while len(recursive_vars) > 0: count_of_previously_resolved_vars = len(nonrecursive_vars) # pylint: disable=C0103 for name, value in recursive_vars.copy().items(): try: nonrecursive_vars[name] = value.format(**nonrecursive_vars) recursive_vars.pop(name) except KeyError: pass if count_of_previously_resolved_vars == len(nonrecursive_vars): raise CircularVariableError() return nonrecursive_vars def __get_variable_types( self, variables: Dict[str, Variable] ) -> Tuple[Dict[str, str], Dict[str, str]]: nonrecursive_vars = {} recursive_vars = {} for name, var in variables.items(): if var.recursive: recursive_vars[name] = var.value else: nonrecursive_vars[name] = var.value return nonrecursive_vars, recursive_vars def __format_task_command(self, task: Task, variables: dict) -> str: if task.use_vars or ( task.use_vars is None and self.__project.settings.get('use_vars') ): try: return task.command.format(**variables) except KeyError as e: raise MalformedTaskError( task.name, f'{e} variable expected in [tool.taskipy.variables]', ) return task.command def __run_command_and_return_exit_code( self, command: str, args: Optional[List[str]] = None ) -> int: if args is None: args = [] if self.__project.runner is not None: command = f'{self.__project.runner} {command}' command_with_args = ' '.join([command] + [shlex.quote(arg) for arg in args]) process = subprocess.Popen( command_with_args, shell=True, cwd=self.__working_dir ) signal.signal(signal.SIGTERM, self.__send_signal_to_task_process(process)) try: process.wait() except KeyboardInterrupt: pass return process.returncode def __send_signal_to_task_process( self, process: subprocess.Popen ) -> Callable[[int, Optional[FrameType]], None]: def signal_handler(signum: int, _frame): # pylint: disable=W0640 psutil_process_wrapper = psutil.Process(process.pid) is_direct_subprocess_a_shell_process = sys.platform != 'darwin' # pylint: disable=C0103 if is_direct_subprocess_a_shell_process: # A shell is created because of Popen(..., shell=True) on linux only # We want here to kill shell's child sub_processes_of_taskipy_shell = psutil_process_wrapper.children() for child_process in sub_processes_of_taskipy_shell: child_process.send_signal(signum) else: process.send_signal(signum) return signal_handler def __get_working_dir(self, task_name: Optional[str] = None) -> Optional[Path]: cwd: Optional[str] = self.__project.settings.get("cwd", None) if task_name is not None: cwd = self.__project.tasks[task_name].workdir or cwd if cwd is not None: path = self.__project.dirpath / cwd if path.exists(): return path return None taskipy-1.14.1/taskipy/variable.py000066400000000000000000000006211472137367500171230ustar00rootroot00000000000000class Variable: def __init__(self, name: str, value: str, recursive: bool) -> None: self.__name = name self.__value = value self.__recursive = recursive @property def name(self) -> str: return self.__name @property def value(self) -> str: return self.__value @property def recursive(self) -> bool: return self.__recursive taskipy-1.14.1/tests/000077500000000000000000000000001472137367500144435ustar00rootroot00000000000000taskipy-1.14.1/tests/__init__.py000066400000000000000000000000001472137367500165420ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/000077500000000000000000000000001472137367500163145ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_settings/000077500000000000000000000000001472137367500247675ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_settings/child_with_pyproject/000077500000000000000000000000001472137367500312045ustar00rootroot00000000000000alternative_calling_folder/000077500000000000000000000000001472137367500364675ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_settings/child_with_pyproject.gitkeep000066400000000000000000000000001472137367500401060ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_settings/child_with_pyproject/alternative_calling_foldertaskipy-1.14.1/tests/fixtures/project_with_cwd_under_settings/child_with_pyproject/pyproject.toml000066400000000000000000000003401472137367500341150ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.settings] cwd = "../child_without_pyproject" [tool.taskipy.tasks] pwd = { cmd = "python -c \"import os; print(os.getcwd())\"" } taskipy-1.14.1/tests/fixtures/project_with_cwd_under_settings/child_without_pyproject/000077500000000000000000000000001472137367500317345ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_settings/child_without_pyproject/.gitkeep000066400000000000000000000000001472137367500333530ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_settings/pyproject.toml000066400000000000000000000003071472137367500277030ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.settings] cwd = "." [tool.taskipy.tasks] pwd = { cmd = "python -c \"import os; print(os.getcwd())\"" } taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task/000077500000000000000000000000001472137367500257365ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task/alternative_calling_folder/000077500000000000000000000000001472137367500333005ustar00rootroot00000000000000.gitkeep000066400000000000000000000000001472137367500346400ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task/alternative_calling_foldertaskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task/pyproject.toml000066400000000000000000000003721472137367500306540ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] pwd = { cmd = "python -c \"import os; print(os.getcwd())\"" } pwdsub = { cmd = "python -c \"import os; print(os.getcwd())\"", cwd = "./subfolder" } taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task/subfolder/000077500000000000000000000000001472137367500277235ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task/subfolder/.gitkeep000066400000000000000000000000001472137367500313420ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task_and_settings/000077500000000000000000000000001472137367500305005ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task_and_settings/global_cwd/000077500000000000000000000000001472137367500325755ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task_and_settings/global_cwd/.gitkeep000066400000000000000000000000001472137367500342140ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task_and_settings/pyproject.toml000066400000000000000000000006031472137367500334130ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.settings] cwd = "./global_cwd" [tool.taskipy.tasks] pwdglobalcwd = { cmd = "python -c \"import os; print(os.getcwd())\"" } pwdpyproject = { cmd = "python -c \"import os; print(os.getcwd())\"", cwd = "." } pwdsub = { cmd = "python -c \"import os; print(os.getcwd())\"", cwd = "./subfolder" } taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task_and_settings/subfolder/000077500000000000000000000000001472137367500324655ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_cwd_under_specific_task_and_settings/subfolder/.gitkeep000066400000000000000000000000001472137367500341040ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_malformed_pyproject/000077500000000000000000000000001472137367500251425ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_malformed_pyproject/pyproject.toml000066400000000000000000000002401472137367500300520ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [[[[]]]] asdfdasfdsaf dsafv,dsa;lfj23[5432-tywersf c======]asfdsafs = sadf2435 taskipy-1.14.1/tests/fixtures/project_with_pre_post_task_hooks/000077500000000000000000000000001472137367500251555ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_pre_post_task_hooks/pyproject.toml000066400000000000000000000007301472137367500300710ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] pre_hello = "echo 'pre_task'" hello = "echo 'hello'" post_hello = "echo 'post_task'" pre_hello_failed_pretask = "echo 'pre_task' && exit 1" hello_failed_pretask = "echo 'hello'" post_hello_failed_pretask = "echo 'post_task'" pre_hello_failed_posttask = "echo 'pre_task'" hello_failed_posttask = "echo 'hello'" post_hello_failed_posttask = "echo 'post_task' && exit 1" taskipy-1.14.1/tests/fixtures/project_with_pyproject_and_tasks/000077500000000000000000000000001472137367500251435ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_pyproject_and_tasks/pyproject.toml000066400000000000000000000004111472137367500300530ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] create_hello_txt = "echo 'hello, world' > hello.txt" exit_17 = "exit 17" print_hello_stdout = "echo 'hello stdout'" print_hello_stderr = ">&2 echo 'hello stderr'" taskipy-1.14.1/tests/fixtures/project_with_pyproject_without_tasks_section/000077500000000000000000000000001472137367500276305ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_pyproject_without_tasks_section/pyproject.toml000066400000000000000000000001201472137367500325350ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" taskipy-1.14.1/tests/fixtures/project_with_task_that_checks_args_passed_with_spaces/000077500000000000000000000000001472137367500313435ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_task_that_checks_args_passed_with_spaces/identify.py000066400000000000000000000003571472137367500335350ustar00rootroot00000000000000import argparse if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--full-name') parser.add_argument('--age') args = parser.parse_args() print(f'name: {args.full_name} age: {args.age}') taskipy-1.14.1/tests/fixtures/project_with_task_that_checks_args_passed_with_spaces/pyproject.toml000066400000000000000000000002061472137367500342550ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] identify = "python identify.py" taskipy-1.14.1/tests/fixtures/project_with_tasks_from_child/000077500000000000000000000000001472137367500244105ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_from_child/child_with_pyproject/000077500000000000000000000000001472137367500306255ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_from_child/child_with_pyproject/pyproject.toml000066400000000000000000000002101472137367500335320ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] hello = 'echo "hello from child"' taskipy-1.14.1/tests/fixtures/project_with_tasks_from_child/child_without_pyproject/000077500000000000000000000000001472137367500313555ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_from_child/child_without_pyproject/.gitkeep000066400000000000000000000000001472137367500327740ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_from_child/pyproject.toml000066400000000000000000000002761472137367500273310ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] print_current_dir_name = 'python -c "from pathlib import Path; print(Path.cwd().name)"' taskipy-1.14.1/tests/fixtures/project_with_tasks_suggestion/000077500000000000000000000000001472137367500244715ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_suggestion/pyproject.toml000066400000000000000000000002541472137367500274060ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] exit_17 = "exit 17" print_hello_stdout = "echo 'hello stdout'" taskipy-1.14.1/tests/fixtures/project_with_tasks_that_accept_arguments/000077500000000000000000000000001472137367500266465ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_that_accept_arguments/count.sh000066400000000000000000000000351472137367500303300ustar00rootroot00000000000000echo the argument count is $#taskipy-1.14.1/tests/fixtures/project_with_tasks_that_accept_arguments/pyproject.toml000066400000000000000000000006021472137367500315600ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] echo_number = "echo the number is " echo_named = "echo got a named argument " echo_args_count = "bash count.sh" pre_echo_on_prehook = "echo the number in prehook is " echo_on_prehook = "echo ok" echo_on_posthook = "echo ok" post_echo_on_posthook = "echo the number in posthook is " taskipy-1.14.1/tests/fixtures/project_with_tasks_that_handle_interrupts/000077500000000000000000000000001472137367500270545ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_that_handle_interrupts/loop_with_sigint_handler.py000066400000000000000000000005621472137367500345070ustar00rootroot00000000000000import signal import sys import time def main(): try: while True: time.sleep(0.1) except KeyboardInterrupt: print('failing gracefully...') def sigterm_handler(sig, frame): print('Shutdown gracefully with sigterm...') sys.exit(123) if __name__ == '__main__': signal.signal(signal.SIGTERM, sigterm_handler) main() loop_without_sigint_handler.py000066400000000000000000000002451472137367500351560ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_that_handle_interruptsimport time import sys def main(): try: while True: time.sleep(.1) except: sys.exit(130) if __name__ == '__main__': main() taskipy-1.14.1/tests/fixtures/project_with_tasks_that_handle_interrupts/pyproject.toml000066400000000000000000000003761472137367500317760ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] run_loop_with_interrupt_handling = "python3 loop_with_sigint_handler.py" run_loop_without_interrupt_handling = "python3 loop_without_sigint_handler.py" taskipy-1.14.1/tests/fixtures/project_with_tasks_that_handle_sigterm/000077500000000000000000000000001472137367500263075ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_that_handle_sigterm/loop_with_sigterm_handler.py000066400000000000000000000004341472137367500341150ustar00rootroot00000000000000import signal import sys import time def main(): while True: time.sleep(0.1) def sigterm_handler(sig, frame): print('Shutdown gracefully with sigterm...') sys.exit(123) if __name__ == '__main__': signal.signal(signal.SIGTERM, sigterm_handler) main() taskipy-1.14.1/tests/fixtures/project_with_tasks_that_handle_sigterm/pyproject.toml000066400000000000000000000002561472137367500312260ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] run_loop_with_sigterm_handling = "python3 loop_with_sigterm_handler.py" taskipy-1.14.1/tests/fixtures/project_with_tasks_to_list/000077500000000000000000000000001472137367500237575ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_tasks_to_list/poetry.lock000066400000000000000000000002501472137367500261500ustar00rootroot00000000000000package = [] [metadata] content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" lock-version = "1.0" python-versions = "*" [metadata.files] taskipy-1.14.1/tests/fixtures/project_with_tasks_to_list/pyproject.toml000066400000000000000000000003301472137367500266670ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" version = "1.0.0" authors = ["foobar"] [tool.taskipy.tasks] one = "echo first task" two = "echo second task" three = "echo third task" taskipy-1.14.1/tests/fixtures/project_with_windows_task/000077500000000000000000000000001472137367500236115ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_with_windows_task/pyproject.toml000066400000000000000000000001641472137367500265260ustar00rootroot00000000000000[tool.poetry] name = "taskipy" description = "tasks runner for python projects" [tool.taskipy.tasks] echo = "echo" taskipy-1.14.1/tests/fixtures/project_without_pyproject/000077500000000000000000000000001472137367500236445ustar00rootroot00000000000000taskipy-1.14.1/tests/fixtures/project_without_pyproject/.keep000066400000000000000000000000001472137367500245570ustar00rootroot00000000000000taskipy-1.14.1/tests/test_taskipy.py000066400000000000000000001037301472137367500175440ustar00rootroot00000000000000import os import platform import random import signal import subprocess import time import unittest import warnings from os import path from typing import List, Optional, Tuple from parameterized import parameterized # type: ignore import psutil # type: ignore from tests.utils.project import ( GenerateProjectFromFixture, GenerateProjectWithPyProjectToml, TempProjectDir ) class TaskipyTestCase(unittest.TestCase): def setUp(self): self._tmp_dirs: List[TempProjectDir] = [] def tearDown(self): for tmp_dir in self._tmp_dirs: tmp_dir.clean() def run_task( self, task: str, args: Optional[List[str]] = None, cwd=os.curdir, ) -> Tuple[int, str, str]: args = args or [] proc = self.start_taskipy_process(task, args=args, cwd=cwd) stdout, stderr = proc.communicate() return proc.returncode, stdout.decode(), str(stderr) def start_taskipy_process( self, task: str, args: Optional[List[str]] = None, cwd=os.curdir, ) -> subprocess.Popen: executable_path = path.abspath('task.bat' if platform.system() == 'Windows' else 'task') args = args or [] return subprocess.Popen([executable_path, task] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) def create_test_dir_from_fixture(self, fixture_name: str): project_generator = GenerateProjectFromFixture(path.join('tests', 'fixtures', fixture_name)) tmp_dir = TempProjectDir(project_generator) self._tmp_dirs.append(tmp_dir) return tmp_dir.path def create_test_dir_with_py_project_toml(self, py_project_toml: str): project_generator = GenerateProjectWithPyProjectToml(py_project_toml) tmp_dir = TempProjectDir(project_generator) self._tmp_dirs.append(tmp_dir) return tmp_dir.path # pylint: disable=invalid-name def assertSubstr(self, substr: str, full_string: str): self.assertTrue(substr in full_string, msg=f'Expected \n "{substr}"\nto be in\n "{full_string}"') # pylint: disable=invalid-name def assertNotSubstr(self, substr: str, full_string: str): self.assertFalse(substr in full_string, msg=f'Expected \n "{substr}"\nnot to be in\n "{full_string}"') # pylint: disable=invalid-name def assertSubstrsInOrder(self, substrs: List[str], full_string: str): self.assertGreater(len(substrs), 0, 'please provide at least one substr') for substr_a, substr_b in zip(substrs[:-1], substrs[1:]): self.assertSubstr(substr_a, full_string) self.assertSubstr(substr_b, full_string) self.assertLess(full_string.find(substr_a), full_string.find(substr_b), msg=f'Expected \n "{substr_a}"\nto appear before\n "{substr_b}"\nin\n "{full_string}"') def assertTerminalTextEqual(self, expected: str, actual: str): expected_without_ansi_chars = expected.encode('ascii', 'ignore') actual_without_ansi_chars = actual.encode('ascii', 'ignore') self.assertEqual(expected_without_ansi_chars, actual_without_ansi_chars) class RunTaskTestCase(TaskipyTestCase): def test_running_task(self): cwd = self.create_test_dir_from_fixture('project_with_pyproject_and_tasks') self.run_task('create_hello_txt', cwd=cwd) with open(path.join(cwd, 'hello.txt'), 'r', encoding='utf-8') as f: hello_file_contents = f.readline().strip() self.assertEqual(hello_file_contents, 'hello, world') def test_exit_code_matches_task_exit_code(self): cwd = self.create_test_dir_from_fixture('project_with_pyproject_and_tasks') exit_code, _, _ = self.run_task('exit_17', cwd=cwd) self.assertEqual(exit_code, 17) def test_stdout_contains_task_stdout(self): cwd = self.create_test_dir_from_fixture('project_with_pyproject_and_tasks') _, stdout, _ = self.run_task('print_hello_stdout', cwd=cwd) self.assertSubstr('hello stdout', stdout) def test_stderr_contains_task_stderr(self): cwd = self.create_test_dir_from_fixture('project_with_pyproject_and_tasks') _, _, stderr = self.run_task('print_hello_stderr', cwd=cwd) self.assertSubstr('hello stderr', stderr) class TaskPrePostHooksTestCase(TaskipyTestCase): def test_running_pre_task_hook(self): cwd = self.create_test_dir_from_fixture('project_with_pre_post_task_hooks') _, stdout, _ = self.run_task('hello', cwd=cwd) self.assertSubstrsInOrder(['pre_task', 'hello'], stdout) def test_running_post_task_hook(self): cwd = self.create_test_dir_from_fixture('project_with_pre_post_task_hooks') _, stdout, _ = self.run_task('hello', cwd=cwd) self.assertSubstrsInOrder(['hello', 'post_task'], stdout) def test_exiting_after_pre_task_hook_if_exit_code_not_0(self): cwd = self.create_test_dir_from_fixture('project_with_pre_post_task_hooks') exit_code, stdout, _ = self.run_task('hello_failed_pretask', cwd=cwd) self.assertSubstr('pre_task', stdout) self.assertNotSubstr('hello', stdout) self.assertEqual(exit_code, 1) def test_exiting_with_post_task_hook_exit_code_if_not_0(self): cwd = self.create_test_dir_from_fixture('project_with_pre_post_task_hooks') exit_code, stdout, _ = self.run_task('hello_failed_posttask', cwd=cwd) self.assertSubstr('post_task', stdout) self.assertEqual(exit_code, 1) class PassArgumentsTestCase(TaskipyTestCase): def test_running_task_with_positional_arguments(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_that_accept_arguments') some_random_number = random.randint(1, 1000) exit_code, stdout, _ = self.run_task('echo_number', args=[f'{some_random_number}'], cwd=cwd) self.assertSubstr(f'the number is {some_random_number}', stdout) self.assertEqual(exit_code, 0) def test_running_task_with_named_arguments(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_that_accept_arguments') exit_code, stdout, _ = self.run_task('echo_named', args=['-h'], cwd=cwd) self.assertSubstr('got a named argument -h', stdout) self.assertEqual(exit_code, 0) def test_running_task_with_multiple_arguments(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_that_accept_arguments') args = ['one', 'two', 'three', 'four', 'five'] exit_code, stdout, _ = self.run_task('echo_args_count', args=args, cwd=cwd) self.assertSubstr('the argument count is 5', stdout) self.assertEqual(exit_code, 0) def test_running_task_with_arguments_with_spaces(self): cwd = self.create_test_dir_from_fixture('project_with_task_that_checks_args_passed_with_spaces') name = 'Roy Sommer' age = random.randrange(1, 100) exit_code, stdout, _ = self.run_task('identify', args=['--full-name', name, '--age', f'{age}'], cwd=cwd) self.assertSubstr(f'name: {name} age: {age}', stdout) self.assertEqual(exit_code, 0) def test_running_task_arguments_not_passed_to_pre_hook(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_that_accept_arguments') some_random_number = random.randint(1, 1000) exit_code, stdout, _ = self.run_task('echo_on_prehook', args=[f'{some_random_number}'], cwd=cwd) self.assertSubstr('the number in prehook is', stdout) self.assertNotSubstr(f'the number in prehook is {some_random_number}', stdout) self.assertEqual(exit_code, 0) def test_running_task_arguments_not_passed_to_post_hook(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_that_accept_arguments') some_random_number = random.randint(1, 1000) exit_code, stdout, _ = self.run_task('echo_on_posthook', args=[f'{some_random_number}'], cwd=cwd) self.assertSubstr('the number in posthook is', stdout) self.assertNotSubstr(f'the number in posthook is {some_random_number}', stdout) self.assertEqual(exit_code, 0) class ListTasksTestCase(TaskipyTestCase): project_tasks_output = "\n".join([ "one echo first task", "two echo second task", "three echo third task", ]) def test_running_task_list(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_to_list') exit_code, stdout, _ = self.run_task('--list', cwd=cwd) self.assertTerminalTextEqual(self.project_tasks_output, stdout.strip()) self.assertEqual(exit_code, 0) def test_running_task_list_with_shorthand(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_to_list') exit_code, stdout, _ = self.run_task('-l', cwd=cwd) self.assertTerminalTextEqual(self.project_tasks_output, stdout.strip()) self.assertEqual(exit_code, 0) def test_running_task_list_before_name(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_to_list') # anything following the flag should be ignored exit_code, stdout, _ = self.run_task('--list', ['one'], cwd=cwd) self.assertTerminalTextEqual(self.project_tasks_output, stdout.strip()) self.assertEqual(exit_code, 0) def test_running_task_list_with_arg(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_to_list') # when --list follows after task name it should be passed as an argument exit_code, stdout, _ = self.run_task('one', ['--list'], cwd=cwd) expected = "first task --list" self.assertTerminalTextEqual(expected, stdout.strip()) self.assertEqual(exit_code, 0) def test_running_task_list_no_tasks(self): py_project_toml = ''' [tool.taskipy.tasks] ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('--list', cwd=cwd) self.assertTerminalTextEqual('no tasks found. create your first task by adding it to your pyproject.toml file under [tool.taskipy.tasks]', stdout.strip()) self.assertEqual(exit_code, 127) def test_running_task_list_no_tasks_section(self): py_project_toml = '' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('--list', cwd=cwd) self.assertTerminalTextEqual('no tasks found. add a [tool.taskipy.tasks] section to your pyproject.toml', stdout.strip()) self.assertEqual(exit_code, 127) class TaskDescriptionTestCase(TaskipyTestCase): def test_running_task_with_description(self): py_project_toml = ''' [tool.taskipy.tasks] print_age = { cmd = "echo age is 29", help = "prints the age" } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) _, stdout, _ = self.run_task('print_age', cwd=cwd) self.assertSubstr('age is 29', stdout) def test_listing_task_with_description(self): py_project_toml = ''' [tool.taskipy.tasks] print_age = { cmd = "echo age is 29", help = "prints the age" } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) _, stdout, _ = self.run_task('--list', cwd=cwd) self.assertSubstr('prints the age', stdout) def test_reject_task_for_not_having_cmd(self): py_project_toml = ''' [tool.taskipy.tasks] print_age = { help = "prints the age" } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('print_age', cwd=cwd) self.assertEqual(exit_code, 1) self.assertSubstr('the task item does not have the "cmd" property', stdout) def test_allow_task_to_not_have_help(self): py_project_toml = ''' [tool.taskipy.tasks] print_age = { cmd = "echo age is 29" } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('print_age', cwd=cwd) self.assertEqual(exit_code, 0) self.assertSubstr('age is 29', stdout) def test_reject_task_that_is_not_string_nor_object(self): py_project_toml = ''' [tool.taskipy.tasks] print_age = 5 ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('print_age', cwd=cwd) self.assertEqual(exit_code, 1) self.assertSubstr('tasks must be strings, or dicts that contain { cmd, cwd, help, use_vars }', stdout) class TaskRunFailTestCase(TaskipyTestCase): def test_exiting_with_code_127_and_printing_if_task_not_found(self): cwd = self.create_test_dir_from_fixture('project_with_pyproject_and_tasks') exit_code, stdout, _ = self.run_task('task_that_does_not_exist', cwd=cwd) self.assertSubstr('could not find task "task_that_does_not_exist"', stdout) self.assertEqual(exit_code, 127) def test_exiting_with_code_127_and_printing_suggestion_if_task_not_found(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_suggestion') exit_code, stdout, _ = self.run_task('stdout', cwd=cwd) self.assertSubstr('could not find task "stdout", did you mean "print_hello_stdout"?', stdout) self.assertEqual(exit_code, 127) def test_exiting_with_code_127_and_printing_if_task_not_found_and_suggestion(self): cwd = self.create_test_dir_from_fixture("project_with_tasks_suggestion") exit_code, stdout, _ = self.run_task("task_that_does_not_exist", cwd=cwd) self.assertEqual('could not find task "task_that_does_not_exist"\n', stdout) self.assertEqual(exit_code, 127) def test_exiting_with_code_127_and_printing_if_no_arg_is_passed(self): cwd = self.create_test_dir_from_fixture('project_with_pyproject_and_tasks') executable_path = path.abspath('task') proc = subprocess.Popen( executable_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd ) stdout, _ = proc.communicate() self.assertSubstr('usage: task', stdout.decode()) self.assertEqual(proc.returncode, 127) def test_exiting_with_code_127_and_printing_if_no_tasks_section(self): cwd = self.create_test_dir_from_fixture('project_with_pyproject_without_tasks_section') exit_code, stdout, _ = self.run_task('task_that_does_not_exist', cwd=cwd) self.assertSubstr('no tasks found. add a [tool.taskipy.tasks] section to your pyproject.toml', stdout) self.assertEqual(exit_code, 127) def test_exiting_with_code_1_and_printing_if_no_pyproject_toml_file_found(self): cwd = self.create_test_dir_from_fixture('project_without_pyproject') exit_code, stdout, _ = self.run_task('some_task', cwd=cwd) self.assertSubstr('no pyproject.toml file found in this directory or parent directories', stdout) self.assertEqual(exit_code, 1) def test_exiting_with_code_1_and_printing_if_pyproject_toml_file_is_malformed(self): cwd = self.create_test_dir_from_fixture('project_with_malformed_pyproject') exit_code, stdout, _ = self.run_task('some_task', cwd=cwd) self.assertSubstr('pyproject.toml file is malformed and could not be read', stdout) self.assertEqual(exit_code, 1) class InterruptingTaskTestCase(TaskipyTestCase): def setUp(self): super().setUp() # suppress resource warnings, as they are false positives caused by psutil warnings.simplefilter('ignore', category=ResourceWarning) def interrupt_task(self, process: subprocess.Popen): psutil_process_wrapper = psutil.Process(process.pid) processes = psutil_process_wrapper.children(recursive=True) innermost_process = next(filter(lambda p: p.name().lower().startswith('python'), processes)) innermost_process.send_signal(signal.SIGINT) def test_handling_sigint_according_to_subprocess_if_it_handles_it_gracefully(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_that_handle_interrupts') process = self.start_taskipy_process('run_loop_with_interrupt_handling', cwd=cwd) time.sleep(.2) self.interrupt_task(process) exit_code = process.wait() self.assertEqual(exit_code, 0) def test_handling_sigint_according_to_subprocess_if_it_does_not_handle_it_gracefully(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_that_handle_interrupts') process = self.start_taskipy_process('run_loop_without_interrupt_handling', cwd=cwd) time.sleep(.2) self.interrupt_task(process) exit_code = process.wait() self.assertEqual(exit_code, 130) def test_sigterm_should_be_sent_to_subprocess(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_that_handle_sigterm') process = self.start_taskipy_process('run_loop_with_sigterm_handling', cwd=cwd) time.sleep(.2) process.send_signal(signal.SIGTERM) exit_code = process.wait() stdout, _ = process.communicate() self.assertEqual(exit_code, 123) self.assertSubstr('sigterm', str(stdout)) class CustomRunnerTestCase(TaskipyTestCase): def test_running_command_with_custom_runner(self): py_project_toml = ''' [tool.taskipy.settings] runner = "time" [tool.taskipy.tasks] print_with_python = "python -c 'print(1337)'" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) _, _, stderr = self.run_task('print_with_python', cwd=cwd) time_cmd_output_format = 'user' self.assertSubstr(time_cmd_output_format, stderr) def test_running_command_with_custom_runner_with_trailing_space(self): py_project_toml = ''' [tool.taskipy.settings] runner = "time " [tool.taskipy.tasks] print_with_python = "python -c 'print(1337)'" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) _, _, stderr = self.run_task('print_with_python', cwd=cwd) time_cmd_output_format = 'user' self.assertSubstr(time_cmd_output_format, stderr) def test_running_command_with_custom_runner_fails_if_custom_runner_is_not_string(self): py_project_toml = ''' [tool.taskipy.settings] runner = 55 [tool.taskipy.tasks] print_with_python = "python -c 'print(1337)'" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('print_with_python', cwd=cwd) self.assertSubstr('invalid value: runner is not a string. please check [tool.taskipy.settings.runner]', stdout) self.assertEqual(exit_code, 1) class TaskFromChildTestCase(TaskipyTestCase): def test_running_parent_pyproject_task_from_child_directory(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_from_child') _, stdout, _ = self.run_task('print_current_dir_name', cwd=path.join(cwd, 'child_without_pyproject')) self.assertSubstr('child_without_pyproject', stdout) def test_find_nearest_pyproject_from_child_directory(self): cwd = self.create_test_dir_from_fixture('project_with_tasks_from_child') _, stdout, _ = self.run_task('hello', cwd=path.join(cwd, 'child_with_pyproject')) self.assertSubstr('hello from child', stdout) class UseVarsTestCase(TaskipyTestCase): def test_use_vars_working(self): py_project_toml = ''' [tool.taskipy.variables] name = "John Doe" [tool.taskipy.tasks] echo = { cmd = "echo hello {name:<10}:", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello John Doe :', stdout) self.assertEqual(exit_code, 0) def test_use_vars_no_param(self): py_project_toml = ''' [tool.taskipy.variables] name = "John Doe" [tool.taskipy.tasks] echo = { cmd = "echo hello {name}" } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello {name}', stdout) self.assertEqual(exit_code, 0) def test_use_vars_param_disabled(self): py_project_toml = ''' [tool.taskipy.variables] name = "John Doe" [tool.taskipy.tasks] echo = { cmd = "echo hello {name}", use_vars = false } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello {name}', stdout) self.assertEqual(exit_code, 0) def test_use_vars_str_task_no_param(self): py_project_toml = ''' [tool.taskipy.variables] name = "John Doe" [tool.taskipy.tasks] echo = "echo hello {name}" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello {name}', stdout) self.assertEqual(exit_code, 0) def test_use_vars_param_malformed(self): py_project_toml = ''' [tool.taskipy.variables] name = "John Doe" [tool.taskipy.tasks] echo = { cmd = "echo hello {name}", use_vars = 1 } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('task\'s "use_vars" arg has to be bool', stdout) self.assertEqual(exit_code, 1) def test_use_vars_missing_var(self): py_project_toml = ''' [tool.taskipy.tasks] echo = { cmd = "echo hello {name}", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr("reason: 'name' variable expected in [tool.taskipy.variables]", stdout) self.assertEqual(exit_code, 1) def test_use_vars_setting(self): py_project_toml = ''' [tool.taskipy.settings] use_vars = true [tool.taskipy.variables] name = "John Doe" [tool.taskipy.tasks] echo = "echo hello {name:<10}:" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello John Doe :', stdout) self.assertEqual(exit_code, 0) def test_use_vars_setting_override_to_false(self): py_project_toml = ''' [tool.taskipy.settings] use_vars = true [tool.taskipy.variables] name = "John Doe" [tool.taskipy.tasks] echo = { cmd = "echo hello {name}:", use_vars = false } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello {name}:', stdout) self.assertEqual(exit_code, 0) def test_use_vars_setting_override_to_true(self): py_project_toml = ''' [tool.taskipy.settings] use_vars = false [tool.taskipy.variables] name = "John Doe" [tool.taskipy.tasks] echo = { cmd = "echo hello {name}:", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello John Doe:', stdout) self.assertEqual(exit_code, 0) class RecursiveVariablesTestCase(TaskipyTestCase): def test_recursive_variables_can_use_other_variables(self): py_project_toml = ''' [tool.taskipy.settings] use_vars = true [tool.taskipy.variables] first_name = "John" last_name = "Doe" full_name = { var = "{first_name} {last_name}", recursive = true } [tool.taskipy.tasks] echo = "echo hello {full_name}" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello John Doe', stdout) self.assertEqual(exit_code, 0) def test_recursive_variables_can_use_other_recursive_variables(self): py_project_toml = ''' [tool.taskipy.settings] use_vars = true [tool.taskipy.variables] first_name = "John" last_name = "Doe" full_name = { var = "{first_name} {last_name}", recursive = true } another_name = { var = "{full_name} another name!", recursive = true } even_another_name = { var = "{another_name} {full_name}", recursive = true } [tool.taskipy.tasks] echo = "echo hello {even_another_name}" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('hello John Doe another name! John Doe', stdout) self.assertEqual(exit_code, 0) def test_error_is_raised_if_a_cycle_is_detected(self): py_project_toml = ''' [tool.taskipy.settings] use_vars = true [tool.taskipy.variables] last_name = { var = "{first_name}", recursive = true } first_name = { var = "{last_name}", recursive = true } [tool.taskipy.tasks] echo = "echo hello {first_name} {last_name}!" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('cannot resolve variables, found variables that depend on each other', stdout) self.assertEqual(exit_code, 127) def test_non_recursive_variables_cant_use_other_variables(self): py_project_toml = ''' [tool.taskipy.settings] use_vars = true [tool.taskipy.variables] first_name = "John" last_name = "Doe" full_name_1 = "{first_name} {last_name}" full_name_2 = { var = "{first_name} {last_name}", recursive = false } [tool.taskipy.tasks] echo = "echo full_name_1: {full_name_1} full_name_2: {full_name_2}!" ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('full_name_1: {first_name} {last_name} full_name_2: {first_name} {last_name}', stdout) self.assertEqual(exit_code, 0) class VariableSchemaTestCase(TaskipyTestCase): def test_variables_can_be_strings(self): py_project_toml = ''' [tool.taskipy.variables] name = "John" [tool.taskipy.tasks] echo = { cmd = "echo {name}", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('John', stdout) self.assertEqual(exit_code, 0) def test_variables_can_be_a_table_with_var_key_and_no_recursive_key(self): py_project_toml = ''' [tool.taskipy.variables] name = { var = "John" } [tool.taskipy.tasks] echo = { cmd = "echo {name}", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('John', stdout) self.assertEqual(exit_code, 0) def test_variables_can_be_a_table_with_var_key_and_recursive_key(self): py_project_toml = ''' [tool.taskipy.variables] name = { var = "John", recursive = true } [tool.taskipy.tasks] echo = { cmd = "echo {name}", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('John', stdout) self.assertEqual(exit_code, 0) def test_variable_table_by_default_is_not_recursive(self): py_project_toml = ''' [tool.taskipy.variables] test = "test var" name = { var = "{test}" } [tool.taskipy.tasks] echo = { cmd = "echo {name}", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('{test}', stdout) self.assertEqual(exit_code, 0) def test_regular_string_variable_are_not_recursive(self): py_project_toml = ''' [tool.taskipy.variables] test = "test var" name = "{test}" [tool.taskipy.tasks] echo = { cmd = "echo {name}", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('{test}', stdout) self.assertEqual(exit_code, 0) @parameterized.expand([(5,), (5.5, ), ([], ), ("false",), ({},)]) def test_other_variable_schemas_are_rejected_with_invalid_variable_error( self, value ): py_project_toml = f''' [tool.taskipy.variables] test = {value} ''' + ''' [tool.taskipy.tasks] echo = { cmd = "echo {test}", use_vars = true } ''' cwd = self.create_test_dir_with_py_project_toml(py_project_toml) exit_code, stdout, _ = self.run_task('echo', cwd=cwd) self.assertSubstr('variable test is invalid', stdout) self.assertEqual(exit_code, 127) class SetCWDTestCase(TaskipyTestCase): def test_project_with_cwd_under_settings(self): cwd = self.create_test_dir_from_fixture("project_with_cwd_under_settings") exit_code, stdout, _ = self.run_task("pwd", cwd=cwd) self.assertTrue(stdout.strip().endswith(cwd)) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwd", cwd=path.join(cwd, "child_without_pyproject")) self.assertTrue(stdout.strip().endswith(cwd)) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwd", cwd=path.join(cwd, "child_with_pyproject")) self.assertTrue(stdout.strip().endswith("child_without_pyproject")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwd", cwd=path.join(cwd, "child_with_pyproject/alternative_calling_folder")) self.assertTrue(stdout.strip().endswith("child_without_pyproject")) self.assertEqual(exit_code, 0) def test_project_with_cwd_under_specific_task(self): cwd = self.create_test_dir_from_fixture("project_with_cwd_under_specific_task") exit_code, stdout, _ = self.run_task("pwd", cwd=cwd) self.assertTrue(stdout.strip().endswith(cwd)) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwd", cwd=path.join(cwd, "subfolder")) self.assertTrue(stdout.strip().endswith("subfolder")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwd", cwd=path.join(cwd, "alternative_calling_folder")) self.assertTrue(stdout.strip().endswith("alternative_calling_folder")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdsub", cwd=cwd) self.assertTrue(stdout.strip().endswith("subfolder")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdsub", cwd=path.join(cwd, "subfolder")) self.assertTrue(stdout.strip().endswith("subfolder")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdsub", cwd=path.join(cwd, "alternative_calling_folder")) self.assertTrue(stdout.strip().endswith("subfolder")) self.assertEqual(exit_code, 0) def test_project_with_cwd_under_specific_task_and_settings(self): cwd = self.create_test_dir_from_fixture("project_with_cwd_under_specific_task_and_settings") exit_code, stdout, _ = self.run_task("pwdglobalcwd", cwd=cwd) self.assertTrue(stdout.strip().endswith("global_cwd")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdglobalcwd", cwd=path.join(cwd, "subfolder")) self.assertTrue(stdout.strip().endswith("global_cwd")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdglobalcwd", cwd=path.join(cwd, "global_cwd")) self.assertTrue(stdout.strip().endswith("global_cwd")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdpyproject", cwd=cwd) self.assertTrue(stdout.strip().endswith(cwd)) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdpyproject", cwd=path.join(cwd, "subfolder")) self.assertTrue(stdout.strip().endswith(cwd)) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdpyproject", cwd=path.join(cwd, "global_cwd")) self.assertTrue(stdout.strip().endswith(cwd)) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdsub", cwd=cwd) self.assertTrue(stdout.strip().endswith("subfolder")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdsub", cwd=path.join(cwd, "subfolder")) self.assertTrue(stdout.strip().endswith("subfolder")) self.assertEqual(exit_code, 0) exit_code, stdout, _ = self.run_task("pwdsub", cwd=path.join(cwd, "global_cwd")) self.assertTrue(stdout.strip().endswith("subfolder")) self.assertEqual(exit_code, 0) taskipy-1.14.1/tests/test_tomli_install.py000066400000000000000000000032721472137367500207320ustar00rootroot00000000000000import subprocess import sys import unittest from typing import Dict, List, Tuple class TomliInstallTestCase(unittest.TestCase): def test_correct_tomli_version_installed(self): python_major, python_minor = self.__get_current_python_version() packages = self.__get_installed_pip_packages() tomli_version = packages.get('tomli') v1_regex = r'1.[0-9]+.[0-9]+' v2_regex = r'2.[0-9]+.[0-9]+' if python_major == 3 and python_minor == 6: self.assertRegex(tomli_version, v1_regex) elif python_major == 3 and python_minor >= 7: self.assertRegex(tomli_version, v2_regex) else: self.fail("Executed with invalid Python version") def __get_current_python_version(self) -> Tuple[int, int]: python_version = sys.version_info return python_version.major, python_version.minor def __get_installed_pip_packages(self) -> Dict[str, str]: cmd = "pip list" process = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, _ = process.communicate() exit_code = process.returncode if exit_code != 0: raise RuntimeError( 'listing pip packages got a non-zero exit code' ) result = {} packages = self.__remove_pip_list_table_header(stdout.splitlines()) for package in packages: decoded_package = package.decode('utf-8') name, version, *_ = decoded_package.split() result[name] = version return result def __remove_pip_list_table_header(self, lines: List[bytes]) -> List[bytes]: return lines[2:] taskipy-1.14.1/tests/test_windows.py000066400000000000000000000015161472137367500175510ustar00rootroot00000000000000import platform import unittest @unittest.skipIf(platform.system() != 'Windows', 'Windows only tests') class WindowsTestCase(unittest.TestCase): def test_windows_path_sanity(self): ''' This test case is a no-op, and exists only to ensure that windows paths work as part of the windows sanity ci test. ''' print('sanity passed - test was run') def test_mslex_install(self): '''Ensure that mslex is installed on Windows''' try: import mslex # type: ignore # pylint: disable=W0611,C0415 except ImportError: self.fail('Unable to import mslex') def test_mslex_import(self): '''Ensure that mslex is used as shlex''' from taskipy.task_runner import shlex # pylint: disable=C0415 self.assertEqual(shlex.__name__, 'mslex') taskipy-1.14.1/tests/utils/000077500000000000000000000000001472137367500156035ustar00rootroot00000000000000taskipy-1.14.1/tests/utils/__init__.py000066400000000000000000000000001472137367500177020ustar00rootroot00000000000000taskipy-1.14.1/tests/utils/project.py000066400000000000000000000022011472137367500176160ustar00rootroot00000000000000import tempfile import shutil import os from os import path from abc import abstractmethod class ProjectDirGenerator: @abstractmethod def generate_project(self, temp_dir: str): pass class GenerateProjectFromFixture(ProjectDirGenerator): def __init__(self, fixture_dir: str): self._fixture_dir: str = fixture_dir def generate_project(self, temp_dir: str): shutil.copytree(self._fixture_dir, temp_dir) class GenerateProjectWithPyProjectToml(ProjectDirGenerator): def __init__(self, py_project_toml: str): self._py_project_toml: str = py_project_toml def generate_project(self, temp_dir: str): os.makedirs(temp_dir) with open(path.join(temp_dir, 'pyproject.toml'), 'w', encoding='utf-8') as f: f.write(self._py_project_toml) class TempProjectDir: def __init__(self, project_dir_generator: ProjectDirGenerator): self._tmpdir: str = tempfile.mktemp() project_dir_generator.generate_project(self._tmpdir) @property def path(self): return self._tmpdir def clean(self): shutil.rmtree(self._tmpdir, ignore_errors=True)