diff --git a/.github/actions/with-docker/action.yml b/.github/actions/with-docker/action.yml index 209d7e85..0c84f267 100644 --- a/.github/actions/with-docker/action.yml +++ b/.github/actions/with-docker/action.yml @@ -21,7 +21,7 @@ runs: GROUP=$(id -gn) GROUP_ID=$(id -g) - docker build . --tag ${TAG} --build-arg K_COMMIT="$(cat deps/wasm-semantics/deps/k_release)" --build-arg PYK_VERSION="$(cat deps/wasm-semantics/deps/pyk_release)" --build-arg USER_ID=${USER_ID} --build-arg GROUP_ID=${GROUP_ID} --build-arg USER=${USER} --build-arg GROUP=${GROUP} + docker build . --tag ${TAG} --build-arg K_COMMIT="$(cat deps/wasm-semantics/deps/k_release)" --build-arg USER_ID=${USER_ID} --build-arg GROUP_ID=${GROUP_ID} --build-arg USER=${USER} --build-arg GROUP=${GROUP} docker run \ --name ${CONTAINER_NAME} \ diff --git a/.github/workflows/test-pr.yml b/.github/workflows/test-pr.yml index d031e2db..db1a28c1 100644 --- a/.github/workflows/test-pr.yml +++ b/.github/workflows/test-pr.yml @@ -7,29 +7,36 @@ concurrency: cancel-in-progress: true jobs: + python-code-quality-checks: + name: 'Code Quality Checks' + runs-on: ubuntu-latest + steps: + - name: 'Check out code' + uses: actions/checkout@v3 + with: + submodules: recursive + - name: 'Install Poetry' + uses: Gr1N/setup-poetry@v8 + - name: 'Build and run code quality checks' + run: make -C kmultiversx check simple-tests: runs-on: [self-hosted, linux, normal] name: 'Simple Tests' + needs: [python-code-quality-checks] timeout-minutes: 30 steps: - - name: Checkout + - name: 'Check out code' uses: actions/checkout@v3 - with: - token: ${{ secrets.JENKINS_GITHUB_PAT }} - - name: Checkout submodules - env: - GITHUB_TOKEN: ${{ secrets.JENKINS_GITHUB_PAT }} - run: | - # https://gist.github.com/taoyuan/bfa3ff87e4b5611b5cbe ; for a repository we don't control the submodules over. - git config --global url."https://github.com/".insteadOf git@github.com: - git config --global url."https://".insteadOf git:// - git submodule update --init --recursive + with: + submodules: recursive - name: 'Set up Docker' uses: ./.github/actions/with-docker with: container-name: elrond-semantics-ci-${{ github.sha }} - name: 'Build' run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make build RELEASE=true -j4' + - name: 'Install Kmultiversx' + run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make poetry-install' - name: 'Run Python Unit Tests' run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make TEST_CONCRETE_BACKEND=llvm unittest-python' - name: 'Run Simple Test' @@ -47,24 +54,18 @@ jobs: needs: [simple-tests] timeout-minutes: 120 steps: - - name: Checkout + - name: 'Check out code' uses: actions/checkout@v3 - with: - token: ${{ secrets.JENKINS_GITHUB_PAT }} - - name: Checkout submodules - env: - GITHUB_TOKEN: ${{ secrets.JENKINS_GITHUB_PAT }} - run: | - # https://gist.github.com/taoyuan/bfa3ff87e4b5611b5cbe ; for a repository we don't control the submodules over. - git config --global url."https://github.com/".insteadOf git@github.com: - git config --global url."https://".insteadOf git:// - git submodule update --init --recursive + with: + submodules: recursive - name: 'Set up Docker' uses: ./.github/actions/with-docker with: container-name: elrond-semantics-ci-${{ github.sha }} - name: 'Build' run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make build RELEASE=true -j4' + - name: 'Install Kmultiversx' + run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make poetry-install' - name: 'Run Basic Feature Test' run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make TEST_CONCRETE_BACKEND=llvm test-elrond-basic-features -j6' - name: 'Run Alloc Feature Test' @@ -80,24 +81,18 @@ jobs: needs: [feature-tests] timeout-minutes: 60 steps: - - name: Checkout + - name: 'Check out code' uses: actions/checkout@v3 - with: - token: ${{ secrets.JENKINS_GITHUB_PAT }} - - name: Checkout submodules - env: - GITHUB_TOKEN: ${{ secrets.JENKINS_GITHUB_PAT }} - run: | - # https://gist.github.com/taoyuan/bfa3ff87e4b5611b5cbe ; for a repository we don't control the submodules over. - git config --global url."https://github.com/".insteadOf git@github.com: - git config --global url."https://".insteadOf git:// - git submodule update --init --recursive + with: + submodules: recursive - name: 'Set up Docker' uses: ./.github/actions/with-docker with: container-name: elrond-semantics-ci-${{ github.sha }} - name: 'Build' run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make build RELEASE=true -j4' + - name: 'Install Kmultiversx' + run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make poetry-install' - name: 'Run Adder Contract Test' run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make TEST_CONCRETE_BACKEND=llvm test-elrond-adder' - name: 'Run Crowdfunding ESDT Contract Test' @@ -115,24 +110,18 @@ jobs: needs: [feature-tests] timeout-minutes: 30 steps: - - name: Checkout + - name: 'Check out code' uses: actions/checkout@v3 - with: - token: ${{ secrets.JENKINS_GITHUB_PAT }} - - name: Checkout submodules - env: - GITHUB_TOKEN: ${{ secrets.JENKINS_GITHUB_PAT }} - run: | - # https://gist.github.com/taoyuan/bfa3ff87e4b5611b5cbe ; for a repository we don't control the submodules over. - git config --global url."https://github.com/".insteadOf git@github.com: - git config --global url."https://".insteadOf git:// - git submodule update --init --recursive + with: + submodules: recursive - name: 'Set up Docker' uses: ./.github/actions/with-docker with: container-name: elrond-semantics-ci-${{ github.sha }} - name: 'Build' run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make build RELEASE=true -j4' + - name: 'Install Kmultiversx' + run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make poetry-install' - name: 'Run Custom Contract Tests' run: docker exec -t elrond-semantics-ci-${GITHUB_SHA} /bin/bash -c 'make TEST_CONCRETE_BACKEND=llvm test-custom-contracts' - name: 'Tear down Docker' diff --git a/Dockerfile b/Dockerfile index a21aa1a8..2b5f7ef0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,9 @@ RUN apt-get update \ python3-pip \ python3-venv +RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/usr python3 - \ + && poetry --version + ARG USER=github-user ARG GROUP=$USER ARG USER_ID=1000 @@ -31,13 +34,7 @@ WORKDIR /home/$USER RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly-2023-03-01 --target wasm32-unknown-unknown ENV PATH=/home/$USER/.cargo/bin:$PATH -ARG PYK_VERSION RUN python3 -m pip install --upgrade pip -RUN pip3 install --user --upgrade \ - cytoolz \ - numpy \ - pycryptodomex \ - git+https://github.com/runtimeverification/pyk.git@${PYK_VERSION} RUN git clone 'https://github.com/WebAssembly/wabt' --branch 1.0.13 --recurse-submodules wabt \ && cd wabt \ diff --git a/Makefile b/Makefile index e21d7051..95115add 100644 --- a/Makefile +++ b/Makefile @@ -262,15 +262,34 @@ $(ELROND_LOADED_JSON): $(ELROND_RUNTIME) # Elrond Tests # ------------ +POETRY := poetry -C kmultiversx +POETRY_RUN := $(POETRY) run -TEST_MANDOS := python3 run_elrond_tests.py +.PHONY: poetry-install +poetry-install: + $(POETRY) install --no-ansi +TEST_MANDOS := $(POETRY_RUN) mandos --definition-dir $(llvm_dir)/mandos-kompiled + +# Cargo resolves dependencies to the latest version that satisfy requirements without taking +# the rustc version into account, which leads to the following error: +# +# > error: package `clap_derive v4.4.0` cannot be built because it requires rustc 1.70.0 or newer, +# > while the currently active rustc version is 1.69.0-nightly +# +# To avoid this, we enforce minimal version resolution before building the contract +mxpy-build/%: + if [ ! -f "$*/Cargo.lock" ]; then \ + cargo generate-lockfile --manifest-path $*/Cargo.toml -Z minimal-versions ; \ + fi + + mxpy contract build "$*" --wasm-symbols --no-wasm-opt ## Mandos Test MANDOS_TESTS_DIR := tests/mandos mandos_tests=$(sort $(wildcard $(MANDOS_TESTS_DIR)/*.scen.json)) -mandos-test: $(llvm_kompiled) +mandos-test: $(llvm_kompiled) poetry-install $(TEST_MANDOS) $(mandos_tests) ## Adder Test @@ -278,8 +297,7 @@ mandos-test: $(llvm_kompiled) ELROND_ADDER_DIR := $(ELROND_CONTRACT_EXAMPLES)/adder elrond_adder_tests=$(shell find $(ELROND_ADDER_DIR) -name "*.scen.json") -test-elrond-adder: $(llvm_kompiled) - mxpy contract build "$(ELROND_ADDER_DIR)" --wasm-symbols +test-elrond-adder: $(llvm_kompiled) poetry-install mxpy-build/$(ELROND_ADDER_DIR) $(TEST_MANDOS) $(elrond_adder_tests) @@ -288,8 +306,7 @@ test-elrond-adder: $(llvm_kompiled) ELROND_CROWDFUNDING_DIR := $(ELROND_CONTRACT_EXAMPLES)/crowdfunding-esdt elrond_crowdfunding_tests=$(shell find $(ELROND_CROWDFUNDING_DIR) -name "*.scen.json") -test-elrond-crowdfunding-esdt: $(llvm_kompiled) - mxpy contract build "$(ELROND_CROWDFUNDING_DIR)" --wasm-symbols +test-elrond-crowdfunding-esdt: $(llvm_kompiled) poetry-install mxpy-build/$(ELROND_CROWDFUNDING_DIR) $(TEST_MANDOS) $(elrond_crowdfunding_tests) ## Multisg Test @@ -297,8 +314,7 @@ test-elrond-crowdfunding-esdt: $(llvm_kompiled) ELROND_MULTISIG_DIR=$(ELROND_CONTRACT_EXAMPLES)/multisig elrond_multisig_tests=$(shell cat tests/multisig.test) -test-elrond-multisig: $(llvm_kompiled) - mxpy contract build "$(ELROND_MULTISIG_DIR)" --wasm-symbols +test-elrond-multisig: $(llvm_kompiled) poetry-install mxpy-build/$(ELROND_MULTISIG_DIR) $(TEST_MANDOS) $(elrond_multisig_tests) ## Basic Feature Test @@ -307,13 +323,12 @@ ELROND_BASIC_FEATURES_DIR=$(ELROND_CONTRACT)/feature-tests/basic-features ELROND_BASIC_FEATURES_WASM=$(ELROND_BASIC_FEATURES_DIR)/output/basic-features.wasm elrond_basic_features_tests=$(shell cat tests/basic_features.test) -$(ELROND_BASIC_FEATURES_WASM): - mxpy contract build "$(ELROND_BASIC_FEATURES_DIR)" --wasm-symbols +$(ELROND_BASIC_FEATURES_WASM): mxpy-build/$(ELROND_BASIC_FEATURES_DIR) # TODO optimize test runner and enable logging test-elrond-basic-features: $(elrond_basic_features_tests:=.mandos) -$(ELROND_BASIC_FEATURES_DIR)/scenarios/%.scen.json.mandos: $(llvm_kompiled) $(ELROND_BASIC_FEATURES_WASM) +$(ELROND_BASIC_FEATURES_DIR)/scenarios/%.scen.json.mandos: $(llvm_kompiled) $(ELROND_BASIC_FEATURES_WASM) poetry-install $(TEST_MANDOS) $(ELROND_BASIC_FEATURES_DIR)/scenarios/$*.scen.json --log-level none ## Alloc Features Test @@ -322,13 +337,12 @@ ELROND_ALLOC_FEATURES_DIR=$(ELROND_CONTRACT)/feature-tests/alloc-features ELROND_ALLOC_FEATURES_WASM=$(ELROND_ALLOC_FEATURES_DIR)/output/alloc-features.wasm elrond_alloc_features_tests=$(shell cat tests/alloc_features.test) -$(ELROND_ALLOC_FEATURES_WASM): - mxpy contract build "$(ELROND_ALLOC_FEATURES_DIR)" --wasm-symbols +$(ELROND_ALLOC_FEATURES_WASM): mxpy-build/$(ELROND_ALLOC_FEATURES_DIR) # TODO optimize test runner and enable logging test-elrond-alloc-features: $(elrond_alloc_features_tests:=.mandos) -$(ELROND_ALLOC_FEATURES_DIR)/scenarios/%.scen.json.mandos: $(llvm_kompiled) $(ELROND_ALLOC_FEATURES_WASM) +$(ELROND_ALLOC_FEATURES_DIR)/scenarios/%.scen.json.mandos: $(llvm_kompiled) $(ELROND_ALLOC_FEATURES_WASM) poetry-install $(TEST_MANDOS) $(ELROND_ALLOC_FEATURES_DIR)/scenarios/$*.scen.json --log-level none # Custom contract tests @@ -343,9 +357,10 @@ ELROND_ADDERCALLER_DIR := tests/contracts/addercaller elrond_addercaller_tests=$(shell find $(ELROND_ADDERCALLER_DIR) -name "*.scen.json") ELROND_MYADDER_DIR := tests/contracts/myadder -test-elrond-addercaller: $(llvm_kompiled) - mxpy contract build "$(ELROND_MYADDER_DIR)" --wasm-symbols - mxpy contract build "$(ELROND_ADDERCALLER_DIR)" --wasm-symbols +test-elrond-addercaller: $(llvm_kompiled) \ + poetry-install \ + mxpy-build/$(ELROND_MYADDER_DIR) \ + mxpy-build/$(ELROND_ADDERCALLER_DIR) $(TEST_MANDOS) $(elrond_addercaller_tests) ## Caller Callee Test @@ -354,9 +369,10 @@ ELROND_CALLER_DIR := tests/contracts/caller ELROND_CALLEE_DIR := tests/contracts/callee elrond_callercallee_tests=$(shell find $(ELROND_CALLER_DIR) -name "*.scen.json") -test-elrond-callercallee: $(llvm_kompiled) - mxpy contract build "$(ELROND_CALLER_DIR)" --wasm-symbols - mxpy contract build "$(ELROND_CALLEE_DIR)" --wasm-symbols +test-elrond-callercallee: $(llvm_kompiled) \ + poetry-install \ + mxpy-build/$(ELROND_CALLER_DIR) \ + mxpy-build/$(ELROND_CALLEE_DIR) $(TEST_MANDOS) $(elrond_callercallee_tests) # Unit Tests diff --git a/README.md b/README.md index 98730b2d..2a8eb863 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,23 @@ Semantics of Elrond and Mandos ============================== -This repository is work-in-progress, and currently a fork of [KWasm](https://github.com/kframework/wasm-semantics). +This repository the semantics of the [MultiversX](https://multiversx.com/) (formerly Elrond) blockchain in K on top of WebAssembly semantics ([KWasm](https://github.com/kframework/wasm-semantics)). -Elrond-specific code is in `elrond.md` and `run_elrond_tests.py`. ## Installation ### Dependencies * Python3 -* WABT +* [WABT v1.0.13](https://github.com/WebAssembly/wabt/tree/1.0.13) * K framework ([version](./deps/wasm-semantics/deps/k_release)) -* `pyk` ([version](./deps/wasm-semantics/deps/pyk_release)) -* Crypto++ +* [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) * Rustup * [mxpy](https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/) -See [Dockerfile](./Dockerfile) for installation. +See [Dockerfile](./Dockerfile) for installation details. -### Build +### Building the semantics Compile the semantics with: @@ -42,6 +40,39 @@ $ make elrond-clean-sources $ make elrond-loaded ``` +### Installing `kmultiversx` + +`kmultiversx` is a Python package providing libraries and CLI tools to interact with the semantics. +To install `kmultiversx` and its dependencies into a virtual environment, run + +``` +# from the 'elrond-semantics' directory +poetry -C kmultiversx install +``` + +After the installation, the Python package `kmultiversx` and CLI tools `mandos` and `foundry` will be available via the `poetry run` command + +``` +poetry -C kmultiversx run mandos --help +poetry -C kmultiversx run foundry --help +``` + +Or you can activate the virtual environment managed by `poetry` and use the commands directly + +``` +poetry -C kmultiversx shell +mandos --help +``` + +Alternatively, you can install `kmultiversx` globally + +``` +make -C kmultiversx build +pip install kmultiversx/dist/*.whl +mandos --help +foundry --help +``` + ## Run To run Mandos tests, first build the contract: @@ -53,13 +84,13 @@ $ mxpy contract build "" --wasm-symbols Then run Mandos scenarios with: ```shell -$ python3 run_elrond_tests.py +poetry -C kmultiversx run mandos --definition .build/defn/llvm/mandos-kompiled ``` -__Important__: `run_elrond_tests.py` makes use of Python modules implemented in the `wasm-semantics` submodule. For the time being, it requires setting the `PYTHONPATH` environment variable. +Or with a globally installed instance ```shell -$ export PYTHONPATH=$(pwd)/deps/wasm-semantics/binary-parser:$PYTHONPATH +mandos --definition .build/defn/llvm/mandos-kompiled ``` Example: @@ -69,7 +100,7 @@ $ mxpy contract build "deps/mx-sdk-rs/contracts/examples/multisig" --wasm-symbol ... INFO:projects.core:Build ran. INFO:projects.core:WASM file generated: /path/to/multisig/output/multisig.wasm -$ python3 run_elrond_tests.py deps/mx-sdk-rs/contracts/examples/multisig/scenarios/changeBoard.scen.json +$ mandos deps/mx-sdk-rs/contracts/examples/multisig/scenarios/changeBoard.scen.json ``` ## Rule Coverage @@ -214,12 +245,13 @@ Build the test contract by executing mxpy contract build ``` -Run the `run_foundry.py` script with the test contract's path as the argument: + +Run the `foundry` tool with the test contract's path as the argument: ```shell -python3 run_foundry.py --directory +foundry --definition-dir .build/defn/llvm/foundry-kompiled --directory ``` -The `run_foundry.py` script will deploy the test contract using the arguments specified in the `foundry.json` file located in the test directory. It will extract the names and argument types of the test endpoints. Subsequently, the script will test these endpoints using random inputs generated with the `hypothesis` library, enabling fuzz testing. If it encounters an input that falsifies the assertions made in the test cases, it attempts to shrink the input and identify a minimal failing example. +The `foundry` tool will deploy the test contract using the arguments specified in the `foundry.json` file located in the test directory. It will extract the names and argument types of the test endpoints. Subsequently, the script will test these endpoints using random inputs generated with the `hypothesis` library, enabling fuzz testing. If it encounters an input that falsifies the assertions made in the test cases, it attempts to shrink the input and identify a minimal failing example. By following these steps, you can efficiently and comprehensively evaluate your Smart Contracts, ensuring their correctness and reliability in various scenarios and inputs. diff --git a/deps/wasm-semantics b/deps/wasm-semantics index bf9b81b8..51b9d186 160000 --- a/deps/wasm-semantics +++ b/deps/wasm-semantics @@ -1 +1 @@ -Subproject commit bf9b81b83ca6d519c2432f62e44106a64179de3c +Subproject commit 51b9d18625ad764a784f6bca985ca57926d8e903 diff --git a/kmultiversx/.cruft.json b/kmultiversx/.cruft.json new file mode 100644 index 00000000..66838433 --- /dev/null +++ b/kmultiversx/.cruft.json @@ -0,0 +1,18 @@ +{ + "template": "https://github.com/runtimeverification/python-project-template", + "commit": "601d5e2a0e8a98c87dcb1ae694d22d76d0114e01", + "checkout": null, + "context": { + "cookiecutter": { + "project_name": "kmultiversx", + "project_slug": "kmultiversx", + "package_name": "kmultiversx", + "version": "0.1.0", + "description": "Python tools for Elrond semantics", + "author_name": "Runtime Verification, Inc.", + "author_email": "contact@runtimeverification.com", + "_template": "https://github.com/runtimeverification/python-project-template" + } + }, + "directory": null +} diff --git a/kmultiversx/.flake8 b/kmultiversx/.flake8 new file mode 100644 index 00000000..a518873c --- /dev/null +++ b/kmultiversx/.flake8 @@ -0,0 +1,7 @@ +[flake8] +max-line-length = 120 +extend-select = B9, TC1 +extend-ignore = B950,E,W1,W2,W3,W4,W5 +per-file-ignores = + */__init__.py: F401 +type-checking-strict = true diff --git a/kmultiversx/.gitignore b/kmultiversx/.gitignore new file mode 100644 index 00000000..98fe5a7f --- /dev/null +++ b/kmultiversx/.gitignore @@ -0,0 +1,3 @@ +/dist/ +__pycache__/ +.coverage diff --git a/kmultiversx/Makefile b/kmultiversx/Makefile new file mode 100644 index 00000000..c7b97436 --- /dev/null +++ b/kmultiversx/Makefile @@ -0,0 +1,92 @@ +POETRY := poetry +POETRY_RUN := $(POETRY) run + + +default: check test-unit + +all: check cov + +.PHONY: clean +clean: + rm -rf dist .coverage cov-* .mypy_cache .pytest_cache + find -type d -name __pycache__ -prune -exec rm -rf {} \; + +.PHONY: build +build: + $(POETRY) build + +.PHONY: poetry-install +poetry-install: + $(POETRY) install + + +# Tests + +TEST_ARGS := + +test: test-all + +test-all: poetry-install + $(POETRY_RUN) pytest src/tests --maxfail=1 --verbose --durations=0 --numprocesses=4 --dist=worksteal $(TEST_ARGS) + +test-unit: poetry-install + $(POETRY_RUN) pytest src/tests/unit --maxfail=1 --verbose $(TEST_ARGS) + +test-integration: poetry-install + $(POETRY_RUN) pytest src/tests/integration --maxfail=1 --verbose --durations=0 --numprocesses=4 --dist=worksteal $(TEST_ARGS) + + +# Coverage + +COV_ARGS := + +cov: cov-all + +cov-%: TEST_ARGS += --cov=kmultiversx --no-cov-on-fail --cov-branch --cov-report=term + +cov-all: TEST_ARGS += --cov-report=html:cov-all-html $(COV_ARGS) +cov-all: test-all + +cov-unit: TEST_ARGS += --cov-report=html:cov-unit-html $(COV_ARGS) +cov-unit: test-unit + +cov-integration: TEST_ARGS += --cov-report=html:cov-integration-html $(COV_ARGS) +cov-integration: test-integration + + +# Checks and formatting + +format: autoflake isort black +check: check-flake8 check-mypy check-autoflake check-isort check-black + +check-flake8: poetry-install + $(POETRY_RUN) flake8 src + +check-mypy: poetry-install + $(POETRY_RUN) mypy src + +autoflake: poetry-install + $(POETRY_RUN) autoflake --quiet --in-place src + +check-autoflake: poetry-install + $(POETRY_RUN) autoflake --quiet --check src + +isort: poetry-install + $(POETRY_RUN) isort src + +check-isort: poetry-install + $(POETRY_RUN) isort --check src + +black: poetry-install + $(POETRY_RUN) black src + +check-black: poetry-install + $(POETRY_RUN) black --check src + + +# Optional tools + +SRC_FILES := $(shell find src -type f -name '*.py') + +pyupgrade: poetry-install + $(POETRY_RUN) pyupgrade --py310-plus $(SRC_FILES) diff --git a/kmultiversx/README.md b/kmultiversx/README.md new file mode 100644 index 00000000..367ea801 --- /dev/null +++ b/kmultiversx/README.md @@ -0,0 +1,23 @@ +# kmultiversx + + +## Installation + +Prerequsites: `python >= 3.10`, `pip >= 20.0.2`, `poetry >= 1.3.2`. + +```bash +make build +pip install dist/*.whl +``` + + +## For Developers + +Use `make` to run common tasks (see the [Makefile](Makefile) for a complete list of available targets). + +* `make build`: Build wheel +* `make check`: Check code style +* `make format`: Format code +* `make test-unit`: Run unit tests + +For interactive use, spawn a shell with `poetry shell` (after `poetry install`), then run an interpreter. diff --git a/kmultiversx/flake.nix b/kmultiversx/flake.nix new file mode 100644 index 00000000..928ab406 --- /dev/null +++ b/kmultiversx/flake.nix @@ -0,0 +1,41 @@ +{ + description = "kmultiversx - Python tools for Elrond semantics"; + inputs = { + nixpkgs.url = "nixpkgs/nixos-22.05"; + flake-utils.url = "github:numtide/flake-utils"; + poetry2nix.url = "github:nix-community/poetry2nix"; + }; + outputs = { self, nixpkgs, flake-utils, poetry2nix }: + let + allOverlays = [ + poetry2nix.overlay + (final: prev: { + kmultiversx = prev.poetry2nix.mkPoetryApplication { + python = prev.python310; + projectDir = ./.; + groups = []; + # We remove `dev` from `checkGroups`, so that poetry2nix does not try to resolve dev dependencies. + checkGroups = []; + }; + }) + ]; + in flake-utils.lib.eachSystem [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ] (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = allOverlays; + }; + in { + packages = rec { + inherit (pkgs) kmultiversx; + default = kmultiversx; + }; + }) // { + overlay = nixpkgs.lib.composeManyExtensions allOverlays; + }; +} diff --git a/kmultiversx/poetry.lock b/kmultiversx/poetry.lock new file mode 100644 index 00000000..23be2cb9 --- /dev/null +++ b/kmultiversx/poetry.lock @@ -0,0 +1,1237 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "autoflake" +version = "2.2.0" +description = "Removes unused imports and unused variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "autoflake-2.2.0-py3-none-any.whl", hash = "sha256:de409b009a34c1c2a7cc2aae84c4c05047f9773594317c6a6968bd497600d4a0"}, + {file = "autoflake-2.2.0.tar.gz", hash = "sha256:62e1f74a0fdad898a96fee6f99fe8241af90ad99c7110c884b35855778412251"}, +] + +[package.dependencies] +pyflakes = ">=3.0.0" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[[package]] +name = "black" +version = "23.7.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, + {file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, + {file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, + {file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, + {file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, + {file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, + {file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, + {file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, + {file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, + {file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, + {file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, + {file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, + {file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, + {file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, + {file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, + {file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "classify-imports" +version = "4.2.0" +description = "Utilities for refactoring imports in python-like syntax." +optional = false +python-versions = ">=3.7" +files = [ + {file = "classify_imports-4.2.0-py2.py3-none-any.whl", hash = "sha256:dbbc264b70a470ed8c6c95976a11dfb8b7f63df44ed1af87328bbed2663f5161"}, + {file = "classify_imports-4.2.0.tar.gz", hash = "sha256:7abfb7ea92149b29d046bd34573d247ba6e68cc28100c801eba4af17964fc40e"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "cmd2" +version = "2.4.3" +description = "cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cmd2-2.4.3-py3-none-any.whl", hash = "sha256:f1988ff2fff0ed812a2d25218a081b0fa0108d45b48ba2a9272bb98091b654e6"}, + {file = "cmd2-2.4.3.tar.gz", hash = "sha256:71873c11f72bd19e2b1db578214716f0d4f7c8fa250093c601265a9a717dee52"}, +] + +[package.dependencies] +attrs = ">=16.3.0" +pyperclip = ">=1.6" +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""} +wcwidth = ">=0.1.7" + +[package.extras] +dev = ["codecov", "doc8", "flake8", "invoke", "mypy", "nox", "pytest (>=4.6)", "pytest-cov", "pytest-mock", "sphinx", "sphinx-autobuild", "sphinx-rtd-theme", "twine (>=1.11)"] +test = ["codecov", "coverage", "gnureadline", "pytest (>=4.6)", "pytest-cov", "pytest-mock"] +validate = ["flake8", "mypy", "types-pkg-resources"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coloredlogs" +version = "15.0.1" +description = "Colored terminal output for Python's logging module" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, + {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, +] + +[package.dependencies] +humanfriendly = ">=9.1" + +[package.extras] +cron = ["capturer (>=2.4)"] + +[[package]] +name = "coverage" +version = "7.3.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db76a1bcb51f02b2007adacbed4c88b6dee75342c37b05d1822815eed19edee5"}, + {file = "coverage-7.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c02cfa6c36144ab334d556989406837336c1d05215a9bdf44c0bc1d1ac1cb637"}, + {file = "coverage-7.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477c9430ad5d1b80b07f3c12f7120eef40bfbf849e9e7859e53b9c93b922d2af"}, + {file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce2ee86ca75f9f96072295c5ebb4ef2a43cecf2870b0ca5e7a1cbdd929cf67e1"}, + {file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68d8a0426b49c053013e631c0cdc09b952d857efa8f68121746b339912d27a12"}, + {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3eb0c93e2ea6445b2173da48cb548364f8f65bf68f3d090404080d338e3a689"}, + {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:90b6e2f0f66750c5a1178ffa9370dec6c508a8ca5265c42fbad3ccac210a7977"}, + {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96d7d761aea65b291a98c84e1250cd57b5b51726821a6f2f8df65db89363be51"}, + {file = "coverage-7.3.0-cp310-cp310-win32.whl", hash = "sha256:63c5b8ecbc3b3d5eb3a9d873dec60afc0cd5ff9d9f1c75981d8c31cfe4df8527"}, + {file = "coverage-7.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:97c44f4ee13bce914272589b6b41165bbb650e48fdb7bd5493a38bde8de730a1"}, + {file = "coverage-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74c160285f2dfe0acf0f72d425f3e970b21b6de04157fc65adc9fd07ee44177f"}, + {file = "coverage-7.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b543302a3707245d454fc49b8ecd2c2d5982b50eb63f3535244fd79a4be0c99d"}, + {file = "coverage-7.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad0f87826c4ebd3ef484502e79b39614e9c03a5d1510cfb623f4a4a051edc6fd"}, + {file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13c6cbbd5f31211d8fdb477f0f7b03438591bdd077054076eec362cf2207b4a7"}, + {file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac440c43e9b479d1241fe9d768645e7ccec3fb65dc3a5f6e90675e75c3f3e3a"}, + {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3c9834d5e3df9d2aba0275c9f67989c590e05732439b3318fa37a725dff51e74"}, + {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4c8e31cf29b60859876474034a83f59a14381af50cbe8a9dbaadbf70adc4b214"}, + {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7a9baf8e230f9621f8e1d00c580394a0aa328fdac0df2b3f8384387c44083c0f"}, + {file = "coverage-7.3.0-cp311-cp311-win32.whl", hash = "sha256:ccc51713b5581e12f93ccb9c5e39e8b5d4b16776d584c0f5e9e4e63381356482"}, + {file = "coverage-7.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:887665f00ea4e488501ba755a0e3c2cfd6278e846ada3185f42d391ef95e7e70"}, + {file = "coverage-7.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d000a739f9feed900381605a12a61f7aaced6beae832719ae0d15058a1e81c1b"}, + {file = "coverage-7.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59777652e245bb1e300e620ce2bef0d341945842e4eb888c23a7f1d9e143c446"}, + {file = "coverage-7.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9737bc49a9255d78da085fa04f628a310c2332b187cd49b958b0e494c125071"}, + {file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5247bab12f84a1d608213b96b8af0cbb30d090d705b6663ad794c2f2a5e5b9fe"}, + {file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ac9a1de294773b9fa77447ab7e529cf4fe3910f6a0832816e5f3d538cfea9a"}, + {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:85b7335c22455ec12444cec0d600533a238d6439d8d709d545158c1208483873"}, + {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:36ce5d43a072a036f287029a55b5c6a0e9bd73db58961a273b6dc11a2c6eb9c2"}, + {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:211a4576e984f96d9fce61766ffaed0115d5dab1419e4f63d6992b480c2bd60b"}, + {file = "coverage-7.3.0-cp312-cp312-win32.whl", hash = "sha256:56afbf41fa4a7b27f6635bc4289050ac3ab7951b8a821bca46f5b024500e6321"}, + {file = "coverage-7.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f297e0c1ae55300ff688568b04ff26b01c13dfbf4c9d2b7d0cb688ac60df479"}, + {file = "coverage-7.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac0dec90e7de0087d3d95fa0533e1d2d722dcc008bc7b60e1143402a04c117c1"}, + {file = "coverage-7.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:438856d3f8f1e27f8e79b5410ae56650732a0dcfa94e756df88c7e2d24851fcd"}, + {file = "coverage-7.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1084393c6bda8875c05e04fce5cfe1301a425f758eb012f010eab586f1f3905e"}, + {file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49ab200acf891e3dde19e5aa4b0f35d12d8b4bd805dc0be8792270c71bd56c54"}, + {file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67e6bbe756ed458646e1ef2b0778591ed4d1fcd4b146fc3ba2feb1a7afd4254"}, + {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f39c49faf5344af36042b293ce05c0d9004270d811c7080610b3e713251c9b0"}, + {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7df91fb24c2edaabec4e0eee512ff3bc6ec20eb8dccac2e77001c1fe516c0c84"}, + {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:34f9f0763d5fa3035a315b69b428fe9c34d4fc2f615262d6be3d3bf3882fb985"}, + {file = "coverage-7.3.0-cp38-cp38-win32.whl", hash = "sha256:bac329371d4c0d456e8d5f38a9b0816b446581b5f278474e416ea0c68c47dcd9"}, + {file = "coverage-7.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b859128a093f135b556b4765658d5d2e758e1fae3e7cc2f8c10f26fe7005e543"}, + {file = "coverage-7.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed8d310afe013db1eedd37176d0839dc66c96bcfcce8f6607a73ffea2d6ba"}, + {file = "coverage-7.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61260ec93f99f2c2d93d264b564ba912bec502f679793c56f678ba5251f0393"}, + {file = "coverage-7.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97af9554a799bd7c58c0179cc8dbf14aa7ab50e1fd5fa73f90b9b7215874ba28"}, + {file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3558e5b574d62f9c46b76120a5c7c16c4612dc2644c3d48a9f4064a705eaee95"}, + {file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37d5576d35fcb765fca05654f66aa71e2808d4237d026e64ac8b397ffa66a56a"}, + {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07ea61bcb179f8f05ffd804d2732b09d23a1238642bf7e51dad62082b5019b34"}, + {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:80501d1b2270d7e8daf1b64b895745c3e234289e00d5f0e30923e706f110334e"}, + {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4eddd3153d02204f22aef0825409091a91bf2a20bce06fe0f638f5c19a85de54"}, + {file = "coverage-7.3.0-cp39-cp39-win32.whl", hash = "sha256:2d22172f938455c156e9af2612650f26cceea47dc86ca048fa4e0b2d21646ad3"}, + {file = "coverage-7.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:60f64e2007c9144375dd0f480a54d6070f00bb1a28f65c408370544091c9bc9e"}, + {file = "coverage-7.3.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:5492a6ce3bdb15c6ad66cb68a0244854d9917478877a25671d70378bdc8562d0"}, + {file = "coverage-7.3.0.tar.gz", hash = "sha256:49dbb19cdcafc130f597d9e04a29d0a032ceedf729e41b181f51cd170e6ee865"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cytoolz" +version = "0.12.2" +description = "Cython implementation of Toolz: High performance functional utilities" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cytoolz-0.12.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bff49986c9bae127928a2f9fd6313146a342bfae8292f63e562f872bd01b871"}, + {file = "cytoolz-0.12.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:908c13f305d34322e11b796de358edaeea47dd2d115c33ca22909c5e8fb036fd"}, + {file = "cytoolz-0.12.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:735147aa41b8eeb104da186864b55e2a6623c758000081d19c93d759cd9523e3"}, + {file = "cytoolz-0.12.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7d352d4de060604e605abdc5c8a5d0429d5f156cb9866609065d3003454d4cea"}, + {file = "cytoolz-0.12.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89247ac220031a4f9f689688bcee42b38fd770d4cce294e5d914afc53b630abe"}, + {file = "cytoolz-0.12.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9070ae35c410d644e6df98a8f69f3ed2807e657d0df2a26b2643127cbf6944a5"}, + {file = "cytoolz-0.12.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:843500cd3e4884b92fd4037912bc42d5f047108d2c986d36352e880196d465b0"}, + {file = "cytoolz-0.12.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6a93644d7996fd696ab7f1f466cd75d718d0a00d5c8118b9fe8c64231dc1f85e"}, + {file = "cytoolz-0.12.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:96796594c770bc6587376e74ddc7d9c982d68f47116bb69d90873db5e0ea88b6"}, + {file = "cytoolz-0.12.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:48425107fbb1af3f0f2410c004f16be10ffc9374358e5600b57fa543f46f8def"}, + {file = "cytoolz-0.12.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:cde6dbb788a4cbc4a80a72aa96386ba4c2b17bdfff3ace0709799adbe16d6476"}, + {file = "cytoolz-0.12.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:68ae7091cc73a752f0b938f15bb193de80ca5edf5ae2ea6360d93d3e9228357b"}, + {file = "cytoolz-0.12.2-cp310-cp310-win32.whl", hash = "sha256:997b7e0960072f6bb445402da162f964ea67387b9f18bda2361edcc026e13597"}, + {file = "cytoolz-0.12.2-cp310-cp310-win_amd64.whl", hash = "sha256:663911786dcde3e4a5d88215c722c531c7548903dc07d418418c0d1c768072c0"}, + {file = "cytoolz-0.12.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a7d8b869ded171f6cdf584fc2fc6ae03b30a0e1e37a9daf213a59857a62ed90"}, + {file = "cytoolz-0.12.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9b28787eaf2174e68f0acb3c66f9c6b98bdfeb0930c0d0b08e1941c7aedc8d27"}, + {file = "cytoolz-0.12.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00547da587f124b32b072ce52dd5e4b37cf199fedcea902e33c67548523e4678"}, + {file = "cytoolz-0.12.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:275d53fd769df2102d6c9fc98e553bd8a9a38926f54d6b20cf29f0dd00bf3b75"}, + {file = "cytoolz-0.12.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5556acde785a61d4cf8b8534ae109b023cbd2f9df65ee2afbe070be47c410f8c"}, + {file = "cytoolz-0.12.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b41a85b9b9a2530b72b0d3d10e383fc3c2647ae88169d557d5e216f881860318"}, + {file = "cytoolz-0.12.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673d6e9e3aa86949343b46ac2b7be266c36e07ce77fa1d40f349e6987a814d6e"}, + {file = "cytoolz-0.12.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81e6a9a8fda78a2f4901d2915b25bf620f372997ca1f20a14f7cefef5ad6f6f4"}, + {file = "cytoolz-0.12.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fa44215bc31675a6380cd896dadb7f2054a7b94cfb87e53e52af844c65406a54"}, + {file = "cytoolz-0.12.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a08b4346350660799d81d4016e748bcb134a9083301d41f9618f64a6077f89f2"}, + {file = "cytoolz-0.12.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2fb740482794a72e2e5fec58e4d9b00dcd5a60a8cef68431ff12f2ba0e0d9a7e"}, + {file = "cytoolz-0.12.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9007bb1290c79402be6b84bcf9e7a622a073859d61fcee146dc7bc47afe328f3"}, + {file = "cytoolz-0.12.2-cp311-cp311-win32.whl", hash = "sha256:a973f5286758f76824ecf19ae1999f6697371a9121c8f163295d181d19a819d7"}, + {file = "cytoolz-0.12.2-cp311-cp311-win_amd64.whl", hash = "sha256:1ce324d1b413636ea5ee929f79637821f13c9e55e9588f38228947294944d2ed"}, + {file = "cytoolz-0.12.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c08094b9e5d1b6dfb0845a0253cc2655ca64ce70d15162dfdb102e28c8993493"}, + {file = "cytoolz-0.12.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baf020f4b708f800b353259cd7575e335a79f1ac912d9dda55b2aa0bf3616e42"}, + {file = "cytoolz-0.12.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4416ee86a87180b6a28e7483102c92debc077bec59c67eda8cc63fc52a218ac0"}, + {file = "cytoolz-0.12.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6ee222671eed5c5b16a5ad2aea07f0a715b8b199ee534834bc1dd2798f1ade7"}, + {file = "cytoolz-0.12.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad92e37be0b106fdbc575a3a669b43b364a5ef334495c9764de4c2d7541f7a99"}, + {file = "cytoolz-0.12.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:460c05238fbfe6d848141669d17a751a46c923f9f0c9fd8a3a462ab737623a44"}, + {file = "cytoolz-0.12.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9e5075e30be626ef0f9bedf7a15f55ed4d7209e832bc314fdc232dbd61dcbf44"}, + {file = "cytoolz-0.12.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:03b58f843f09e73414e82e57f7e8d88f087eaabf8f276b866a40661161da6c51"}, + {file = "cytoolz-0.12.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5e4e612b7ecc9596e7c859cd9e0cd085e6d0c576b4f0d917299595eb56bf9c05"}, + {file = "cytoolz-0.12.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:08a0e03f287e45eb694998bb55ac1643372199c659affa8319dfbbdec7f7fb3c"}, + {file = "cytoolz-0.12.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b029bdd5a8b6c9a7c0e8fdbe4fc25ffaa2e09b77f6f3462314696e3a20511829"}, + {file = "cytoolz-0.12.2-cp36-cp36m-win32.whl", hash = "sha256:18580d060fa637ff01541640ecde6de832a248df02b8fb57e6dd578f189d62c7"}, + {file = "cytoolz-0.12.2-cp36-cp36m-win_amd64.whl", hash = "sha256:97cf514a9f3426228d8daf880f56488330e4b2948a6d183a106921217850d9eb"}, + {file = "cytoolz-0.12.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18a0f838677f9510aef0330c0096778dd6406d21d4ff9504bf79d85235a18460"}, + {file = "cytoolz-0.12.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb081b2b02bf4405c804de1ece6f904916838ab0e057f1446e4ac12fac827960"}, + {file = "cytoolz-0.12.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57233e1600560ceb719bed759dc78393edd541b9a3e7fefc3079abd83c26a6ea"}, + {file = "cytoolz-0.12.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0295289c4510efa41174850e75bc9188f82b72b1b54d0ea57d1781729c2924d5"}, + {file = "cytoolz-0.12.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a92aab8dd1d427ac9bc7480cfd3481dbab0ef024558f2f5a47de672d8a5ffaa"}, + {file = "cytoolz-0.12.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d3495235af09f21aa92a7cdd51504bda640b108b6be834448b774f52852c09"}, + {file = "cytoolz-0.12.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9c690b359f503f18bf1c46a6456370e4f6f3fc4320b8774ae69c4f85ecc6c94"}, + {file = "cytoolz-0.12.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:481e3129a76ea01adcc0e7097ccb8dbddab1cfc40b6f0e32c670153512957c0f"}, + {file = "cytoolz-0.12.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:55e94124af9c8fbb1df54195cc092688fdad0765641b738970b6f1d5ea72e776"}, + {file = "cytoolz-0.12.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5616d386dfbfba7c39e9418ba668c734f6ceaacc0130877e8a100cad11e6838b"}, + {file = "cytoolz-0.12.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:732d08228fa8d366fec284f7032cc868d28a99fa81fc71e3adf7ecedbcf33a0f"}, + {file = "cytoolz-0.12.2-cp37-cp37m-win32.whl", hash = "sha256:f039c5373f7b314b151432c73219216857b19ab9cb834f0eb5d880f74fc7851c"}, + {file = "cytoolz-0.12.2-cp37-cp37m-win_amd64.whl", hash = "sha256:246368e983eaee9851b15d7755f82030eab4aa82098d2a34f6bef9c689d33fcc"}, + {file = "cytoolz-0.12.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:81074edf3c74bc9bd250d223408a5df0ff745d1f7a462597536cd26b9390e2d6"}, + {file = "cytoolz-0.12.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:960d85ebaa974ecea4e71fa56d098378fa51fd670ee744614cbb95bf95e28fc7"}, + {file = "cytoolz-0.12.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c8d0dff4865da54ae825d43e1721925721b19f3b9aca8e730c2ce73dee2c630"}, + {file = "cytoolz-0.12.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a9d12436fd64937bd2c9609605f527af7f1a8db6e6637639b44121c0fe715d6"}, + {file = "cytoolz-0.12.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd461e402e24929d866f05061d2f8337e3a8456e75e21b72c125abff2477c7f7"}, + {file = "cytoolz-0.12.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0568d4da0a9ee9f9f5ab318f6501557f1cfe26d18c96c8e0dac7332ae04c6717"}, + {file = "cytoolz-0.12.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:101b5bd32badfc8b1f9c7be04ba3ae04fb47f9c8736590666ce9449bff76e0b1"}, + {file = "cytoolz-0.12.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8bb624dbaef4661f5e3625c1e39ad98ecceef281d1380e2774d8084ad0810275"}, + {file = "cytoolz-0.12.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3e993804e6b04113d61fdb9541b6df2f096ec265a506dad7437517470919c90f"}, + {file = "cytoolz-0.12.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ab911033e5937fc221a2c165acce7f66ae5ac9d3e54bec56f3c9c197a96be574"}, + {file = "cytoolz-0.12.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6de6a4bdfaee382c2de2a3580b3ae76fce6105da202bbd835e5efbeae6a9c6e"}, + {file = "cytoolz-0.12.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9480b4b327be83c4d29cb88bcace761b11f5e30198ffe2287889455c6819e934"}, + {file = "cytoolz-0.12.2-cp38-cp38-win32.whl", hash = "sha256:4180b2785d1278e6abb36a72ac97c92432db53fa2df00ee943d2c15a33627d31"}, + {file = "cytoolz-0.12.2-cp38-cp38-win_amd64.whl", hash = "sha256:d0086ba8d41d73647b13087a3ca9c020f6bfec338335037e8f5172b4c7c8dce5"}, + {file = "cytoolz-0.12.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d29988bde28a90a00367edcf92afa1a2f7ecf43ea3ae383291b7da6d380ccc25"}, + {file = "cytoolz-0.12.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24c0d71e9ac91f4466b1bd280f7de43aa4d94682daaf34d85d867a9b479b87cc"}, + {file = "cytoolz-0.12.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa436abd4ac9ca71859baf5794614e6ec8fa27362f0162baedcc059048da55f7"}, + {file = "cytoolz-0.12.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45c7b4eac7571707269ebc2893facdf87e359cd5c7cfbfa9e6bd8b33fb1079c5"}, + {file = "cytoolz-0.12.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:294d24edc747ef4e1b28e54365f713becb844e7898113fafbe3e9165dc44aeea"}, + {file = "cytoolz-0.12.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:478051e5ef8278b2429864c8d148efcebdc2be948a61c9a44757cd8c816c98f5"}, + {file = "cytoolz-0.12.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14108cafb140dd68fdda610c2bbc6a37bf052cd48cfebf487ed44145f7a2b67f"}, + {file = "cytoolz-0.12.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fef7b602ccf8a3c77ab483479ccd7a952a8c5bb1c263156671ba7aaa24d1035"}, + {file = "cytoolz-0.12.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9bf51354e15520715f068853e6ab8190e77139940e8b8b633bdb587956a08fb0"}, + {file = "cytoolz-0.12.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:388f840fd911d61a96e9e595eaf003f9dc39e847c9060b8e623ab29e556f009b"}, + {file = "cytoolz-0.12.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a67f75cc51a2dc7229a8ac84291e4d61dc5abfc8940befcf37a2836d95873340"}, + {file = "cytoolz-0.12.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63b31345e20afda2ae30dba246955517a4264464d75e071fc2fa641e88c763ec"}, + {file = "cytoolz-0.12.2-cp39-cp39-win32.whl", hash = "sha256:f6e86ac2b45a95f75c6f744147483e0fc9697ce7dfe1726083324c236f873f8b"}, + {file = "cytoolz-0.12.2-cp39-cp39-win_amd64.whl", hash = "sha256:5998f81bf6a2b28a802521efe14d9fc119f74b64e87b62ad1b0e7c3d8366d0c7"}, + {file = "cytoolz-0.12.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:593e89e2518eaf81e96edcc9ef2c5fca666e8fc922b03d5cb7a7b8964dbee336"}, + {file = "cytoolz-0.12.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff451d614ca1d4227db0ffa627fb51df71968cf0d9baf0210528dad10fdbc3ab"}, + {file = "cytoolz-0.12.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad9ea4a50d2948738351790047d45f2b1a023facc01bf0361988109b177e8b2f"}, + {file = "cytoolz-0.12.2-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbe038bb78d599b5a29d09c438905defaa615a522bc7e12f8016823179439497"}, + {file = "cytoolz-0.12.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d494befe648c13c98c0f3d56d05489c839c9228a32f58e9777305deb6c2c1cee"}, + {file = "cytoolz-0.12.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c26805b6c8dc8565ed91045c44040bf6c0fe5cb5b390c78cd1d9400d08a6cd39"}, + {file = "cytoolz-0.12.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df4e32badb2ccf1773e1e74020b7e3b8caf9e92f842c6be7d14888ecdefc2c6c"}, + {file = "cytoolz-0.12.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce7889dc3701826d519ede93cdff11940fb5567dbdc165dce0e78047eece02b7"}, + {file = "cytoolz-0.12.2-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c820608e7077416f766b148d75e158e454881961881b657cff808529d261dd24"}, + {file = "cytoolz-0.12.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:698da4fa1f7baeea0607738cb1f9877ed1ba50342b29891b0223221679d6f729"}, + {file = "cytoolz-0.12.2.tar.gz", hash = "sha256:31d4b0455d72d914645f803d917daf4f314d115c70de0578d3820deb8b101f66"}, +] + +[package.dependencies] +toolz = ">=0.8.0" + +[package.extras] +cython = ["cython"] + +[[package]] +name = "exceptiongroup" +version = "1.1.3" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.0.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.7" +files = [ + {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, + {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "filelock" +version = "3.12.2" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, +] + +[package.extras] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "flake8" +version = "6.1.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" + +[[package]] +name = "flake8-bugbear" +version = "23.7.10" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-bugbear-23.7.10.tar.gz", hash = "sha256:0ebdc7d8ec1ca8bd49347694562381f099f4de2f8ec6bda7a7dca65555d9e0d4"}, + {file = "flake8_bugbear-23.7.10-py3-none-any.whl", hash = "sha256:d99d005114020fbef47ed5e4aebafd22f167f9a0fbd0d8bf3c9e90612cb25c34"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +flake8 = ">=6.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] + +[[package]] +name = "flake8-comprehensions" +version = "3.14.0" +description = "A flake8 plugin to help you write better list/set/dict comprehensions." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_comprehensions-3.14.0-py3-none-any.whl", hash = "sha256:7b9d07d94aa88e62099a6d1931ddf16c344d4157deedf90fe0d8ee2846f30e97"}, + {file = "flake8_comprehensions-3.14.0.tar.gz", hash = "sha256:81768c61bfc064e1a06222df08a2580d97de10cb388694becaf987c331c6c0cf"}, +] + +[package.dependencies] +flake8 = ">=3.0,<3.2.0 || >3.2.0" + +[[package]] +name = "flake8-quotes" +version = "3.3.2" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.3.2.tar.gz", hash = "sha256:6e26892b632dacba517bf27219c459a8396dcfac0f5e8204904c5a4ba9b480e1"}, +] + +[package.dependencies] +flake8 = "*" + +[[package]] +name = "flake8-type-checking" +version = "2.4.1" +description = "A flake8 plugin for managing type-checking imports & forward references" +optional = false +python-versions = ">=3.8" +files = [ + {file = "flake8_type_checking-2.4.1-py3-none-any.whl", hash = "sha256:fe75cfe668e3bf6914dd2ba36579bbe6c82f3795127774e7584ecc8c9f7379e5"}, + {file = "flake8_type_checking-2.4.1.tar.gz", hash = "sha256:f3c7023114dee2ec2e8282a9080206ffd8fdd61205626593aa5c801bc2f8035d"}, +] + +[package.dependencies] +classify-imports = "*" +flake8 = "*" + +[[package]] +name = "graphviz" +version = "0.20.1" +description = "Simple Python interface for Graphviz" +optional = false +python-versions = ">=3.7" +files = [ + {file = "graphviz-0.20.1-py3-none-any.whl", hash = "sha256:587c58a223b51611c0cf461132da386edd896a029524ca61a1462b880bf97977"}, + {file = "graphviz-0.20.1.zip", hash = "sha256:8c58f14adaa3b947daf26c19bc1e98c4e0702cdc31cf99153e6f06904d492bf8"}, +] + +[package.extras] +dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] +docs = ["sphinx (>=5)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +test = ["coverage", "mock (>=4)", "pytest (>=7)", "pytest-cov", "pytest-mock (>=3)"] + +[[package]] +name = "humanfriendly" +version = "10.0" +description = "Human friendly output for text interfaces using Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, + {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, +] + +[package.dependencies] +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} + +[[package]] +name = "hypothesis" +version = "6.82.6" +description = "A library for property-based testing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "hypothesis-6.82.6-py3-none-any.whl", hash = "sha256:e99c445140e43f1cceda07b569f2f2d920d95435c6b0e6b507b35b01bb025e9d"}, + {file = "hypothesis-6.82.6.tar.gz", hash = "sha256:f52ac4180a16208224e3d648fbf0fef8b9ca24863ba4b41bfef30a78c42646bd"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +sortedcontainers = ">=2.1.0,<3.0.0" + +[package.extras] +all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "django (>=3.2)", "dpcontracts (>=0.4)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.17.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2023.3)"] +cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] +codemods = ["libcst (>=0.3.16)"] +dateutil = ["python-dateutil (>=1.4)"] +django = ["django (>=3.2)"] +dpcontracts = ["dpcontracts (>=0.4)"] +ghostwriter = ["black (>=19.10b0)"] +lark = ["lark (>=0.10.1)"] +numpy = ["numpy (>=1.17.3)"] +pandas = ["pandas (>=1.1)"] +pytest = ["pytest (>=4.6)"] +pytz = ["pytz (>=2014.1)"] +redis = ["redis (>=3.0.0)"] +zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2023.3)"] + +[[package]] +name = "importlib-metadata" +version = "6.8.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, + {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.12.0" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + +[[package]] +name = "linkify-it-py" +version = "2.0.2" +description = "Links recognition library with FULL unicode support." +optional = false +python-versions = ">=3.7" +files = [ + {file = "linkify-it-py-2.0.2.tar.gz", hash = "sha256:19f3060727842c254c808e99d465c80c49d2c7306788140987a1a7a29b0d6ad2"}, + {file = "linkify_it_py-2.0.2-py3-none-any.whl", hash = "sha256:a3a24428f6c96f27370d7fe61d2ac0be09017be5190d68d8658233171f1b6541"}, +] + +[package.dependencies] +uc-micro-py = "*" + +[package.extras] +benchmark = ["pytest", "pytest-benchmark"] +dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] +doc = ["myst-parser", "sphinx", "sphinx-book-theme"] +test = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "markdown-it-py" +version = "2.2.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.7" +files = [ + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, +] + +[package.dependencies] +linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""} +mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""} +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[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 = "mdit-py-plugins" +version = "0.4.0" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, + {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mypy" +version = "1.0.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mypy-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:71a808334d3f41ef011faa5a5cd8153606df5fc0b56de5b2e89566c8093a0c9a"}, + {file = "mypy-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:920169f0184215eef19294fa86ea49ffd4635dedfdea2b57e45cb4ee85d5ccaf"}, + {file = "mypy-1.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27a0f74a298769d9fdc8498fcb4f2beb86f0564bcdb1a37b58cbbe78e55cf8c0"}, + {file = "mypy-1.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:65b122a993d9c81ea0bfde7689b3365318a88bde952e4dfa1b3a8b4ac05d168b"}, + {file = "mypy-1.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:5deb252fd42a77add936b463033a59b8e48eb2eaec2976d76b6878d031933fe4"}, + {file = "mypy-1.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2013226d17f20468f34feddd6aae4635a55f79626549099354ce641bc7d40262"}, + {file = "mypy-1.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:48525aec92b47baed9b3380371ab8ab6e63a5aab317347dfe9e55e02aaad22e8"}, + {file = "mypy-1.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96b8a0c019fe29040d520d9257d8c8f122a7343a8307bf8d6d4a43f5c5bfcc8"}, + {file = "mypy-1.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:448de661536d270ce04f2d7dddaa49b2fdba6e3bd8a83212164d4174ff43aa65"}, + {file = "mypy-1.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:d42a98e76070a365a1d1c220fcac8aa4ada12ae0db679cb4d910fabefc88b994"}, + {file = "mypy-1.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64f48c6176e243ad015e995de05af7f22bbe370dbb5b32bd6988438ec873919"}, + {file = "mypy-1.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fdd63e4f50e3538617887e9aee91855368d9fc1dea30da743837b0df7373bc4"}, + {file = "mypy-1.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dbeb24514c4acbc78d205f85dd0e800f34062efcc1f4a4857c57e4b4b8712bff"}, + {file = "mypy-1.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a2948c40a7dd46c1c33765718936669dc1f628f134013b02ff5ac6c7ef6942bf"}, + {file = "mypy-1.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bc8d6bd3b274dd3846597855d96d38d947aedba18776aa998a8d46fabdaed76"}, + {file = "mypy-1.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:17455cda53eeee0a4adb6371a21dd3dbf465897de82843751cf822605d152c8c"}, + {file = "mypy-1.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e831662208055b006eef68392a768ff83596035ffd6d846786578ba1714ba8f6"}, + {file = "mypy-1.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e60d0b09f62ae97a94605c3f73fd952395286cf3e3b9e7b97f60b01ddfbbda88"}, + {file = "mypy-1.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:0af4f0e20706aadf4e6f8f8dc5ab739089146b83fd53cb4a7e0e850ef3de0bb6"}, + {file = "mypy-1.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:24189f23dc66f83b839bd1cce2dfc356020dfc9a8bae03978477b15be61b062e"}, + {file = "mypy-1.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93a85495fb13dc484251b4c1fd7a5ac370cd0d812bbfc3b39c1bafefe95275d5"}, + {file = "mypy-1.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f546ac34093c6ce33f6278f7c88f0f147a4849386d3bf3ae193702f4fe31407"}, + {file = "mypy-1.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c6c2ccb7af7154673c591189c3687b013122c5a891bb5651eca3db8e6c6c55bd"}, + {file = "mypy-1.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:15b5a824b58c7c822c51bc66308e759243c32631896743f030daf449fe3677f3"}, + {file = "mypy-1.0.1-py3-none-any.whl", hash = "sha256:eda5c8b9949ed411ff752b9a01adda31afe7eae1e53e946dbdf9db23865e66c4"}, + {file = "mypy-1.0.1.tar.gz", hash = "sha256:28cea5a6392bb43d266782983b5a4216c25544cd7d80be681a155ddcdafd152d"}, +] + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "0.4.4" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +optional = false +python-versions = ">=2.7" +files = [ + {file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"}, +] + +[[package]] +name = "numpy" +version = "1.25.2" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"}, + {file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"}, + {file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"}, + {file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"}, + {file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"}, + {file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"}, + {file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"}, + {file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"}, + {file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"}, + {file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"}, + {file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"}, + {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, +] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "3.10.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, + {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + +[[package]] +name = "pluggy" +version = "1.2.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "psutil" +version = "5.9.5" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"}, + {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"}, + {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"}, + {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"}, + {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"}, + {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"}, + {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"}, + {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "py-wasm" +version = "0.1.0-alpha.0" +description = "" +optional = false +python-versions = ">=3.5, <4" +files = [ + {file = "0.1.0-alpha.0.tar.gz", hash = "sha256:758fb1ab880365bc4d0fcca9290ed7ca5226de8bc6df194d434e9a6a43a9a75a"}, +] + +[package.dependencies] +cytoolz = {version = ">=0.9.0,<1.0.0", markers = "implementation_name == \"cpython\""} +mypy_extensions = ">=0.4.1,<1.0.0" +numpy = ">=1.16.0,<2" +toolz = {version = ">0.9.0,<1", markers = "implementation_name == \"pypy\""} + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "ipython", "pytest-watch (>=4.1.0,<5)", "twine", "wheel"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx_rtd_theme (>=0.1.9)"] +lint = ["flake8 (==3.6.0)", "isort (>=4.3.9,<5)", "mypy (==0.660)"] +test = ["hypothesis (>=3.88.3,<4)", "pytest (==4.1.0)", "pytest-watch (==4.2.0)", "pytest-xdist", "tox (>=2.9.1,<3)"] + +[package.source] +type = "url" +url = "https://github.com/runtimeverification/py-wasm/archive/refs/tags/0.1.0-alpha.0.tar.gz" + +[[package]] +name = "pybind11" +version = "2.11.1" +description = "Seamless operability between C++11 and Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pybind11-2.11.1-py3-none-any.whl", hash = "sha256:33cdd02a6453380dd71cc70357ce388ad1ee8d32bd0e38fc22b273d050aa29b3"}, + {file = "pybind11-2.11.1.tar.gz", hash = "sha256:00cd59116a6e8155aecd9174f37ba299d1d397ed4a6b86ac1dfe01b3e40f2cc4"}, +] + +[package.extras] +global = ["pybind11-global (==2.11.1)"] + +[[package]] +name = "pycodestyle" +version = "2.11.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"}, + {file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"}, +] + +[[package]] +name = "pycryptodomex" +version = "3.18.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodomex-3.18.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:160a39a708c36fa0b168ab79386dede588e62aec06eb505add870739329aecc6"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c2953afebf282a444c51bf4effe751706b4d0d63d7ca2cc51db21f902aa5b84e"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ba95abd563b0d1b88401658665a260852a8e6c647026ee6a0a65589287681df8"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:192306cf881fe3467dda0e174a4f47bb3a8bb24b90c9cdfbdc248eec5fc0578c"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:f9ab5ef0718f6a8716695dea16d83b671b22c45e9c0c78fd807c32c0192e54b5"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-win32.whl", hash = "sha256:50308fcdbf8345e5ec224a5502b4215178bdb5e95456ead8ab1a69ffd94779cb"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-win_amd64.whl", hash = "sha256:4d9379c684efea80fdab02a3eb0169372bca7db13f9332cb67483b8dc8b67c37"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5594a125dae30d60e94f37797fc67ce3c744522de7992c7c360d02fdb34918f8"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8ff129a5a0eb5ff16e45ca4fa70a6051da7f3de303c33b259063c19be0c43d35"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:3d9314ac785a5b75d5aaf924c5f21d6ca7e8df442e5cf4f0fefad4f6e284d422"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:f237278836dda412a325e9340ba2e6a84cb0f56b9244781e5b61f10b3905de88"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac614363a86cc53d8ba44b6c469831d1555947e69ab3276ae8d6edc219f570f7"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:302a8f37c224e7b5d72017d462a2be058e28f7be627bdd854066e16722d0fc0c"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:6421d23d6a648e83ba2670a352bcd978542dad86829209f59d17a3f087f4afef"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84e105787f5e5d36ec6a581ff37a1048d12e638688074b2a00bcf402f9aa1c2"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6875eb8666f68ddbd39097867325bd22771f595b4e2b0149739b5623c8bf899b"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:27072a494ce621cc7a9096bbf60ed66826bb94db24b49b7359509e7951033e74"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:1949e09ea49b09c36d11a951b16ff2a05a0ffe969dda1846e4686ee342fe8646"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6ed3606832987018615f68e8ed716a7065c09a0fe94afd7c9ca1b6777f0ac6eb"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-win32.whl", hash = "sha256:d56c9ec41258fd3734db9f5e4d2faeabe48644ba9ca23b18e1839b3bdf093222"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-win_amd64.whl", hash = "sha256:e00a4bacb83a2627e8210cb353a2e31f04befc1155db2976e5e239dd66482278"}, + {file = "pycryptodomex-3.18.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2dc4eab20f4f04a2d00220fdc9258717b82d31913552e766d5f00282c031b70a"}, + {file = "pycryptodomex-3.18.0-pp27-pypy_73-win32.whl", hash = "sha256:75672205148bdea34669173366df005dbd52be05115e919551ee97171083423d"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bec6c80994d4e7a38312072f89458903b65ec99bed2d65aa4de96d997a53ea7a"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d35a8ffdc8b05e4b353ba281217c8437f02c57d7233363824e9d794cf753c419"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f0a46bee539dae4b3dfe37216f678769349576b0080fdbe431d19a02da42ff"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:71687eed47df7e965f6e0bf3cadef98f368d5221f0fb89d2132effe1a3e6a194"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:73d64b32d84cf48d9ec62106aa277dbe99ab5fbfd38c5100bc7bddd3beb569f7"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbdcce0a226d9205560a5936b05208c709b01d493ed8307792075dedfaaffa5f"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58fc0aceb9c961b9897facec9da24c6a94c5db04597ec832060f53d4d6a07196"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:215be2980a6b70704c10796dd7003eb4390e7be138ac6fb8344bf47e71a8d470"}, + {file = "pycryptodomex-3.18.0.tar.gz", hash = "sha256:3e3ecb5fe979e7c1bb0027e518340acf7ee60415d79295e5251d13c68dde576e"}, +] + +[[package]] +name = "pyflakes" +version = "3.1.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, +] + +[[package]] +name = "pygments" +version = "2.16.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyk" +version = "0.1.398" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.dependencies] +cmd2 = "^2.4.2" +coloredlogs = "^15.0.1" +filelock = "^3.9.0" +graphviz = "^0.20.1" +psutil = "^5.9.4" +pybind11 = "^2.10.3" +textual = "^0.27.0" +tomli = "^2.0.1" + +[package.source] +type = "git" +url = "https://github.com/runtimeverification/pyk.git" +reference = "v0.1.398" +resolved_reference = "ce44c2336bfe1ca1d3b94b0f8c003bf16c8d4699" + +[[package]] +name = "pykwasm" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.10" +files = [] +develop = false + +[package.dependencies] +cytoolz = "^0.12.1" +numpy = "^1.24.2" +py-wasm = {url = "https://github.com/runtimeverification/py-wasm/archive/refs/tags/0.1.0-alpha.0.tar.gz"} +pyk = {git = "https://github.com/runtimeverification/pyk.git", tag = "v0.1.398"} + +[package.source] +type = "directory" +url = "../deps/wasm-semantics/pykwasm" + +[[package]] +name = "pyperclip" +version = "1.8.2" +description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" +optional = false +python-versions = "*" +files = [ + {file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"}, +] + +[[package]] +name = "pyreadline3" +version = "3.4.1" +description = "A python implementation of GNU readline." +optional = false +python-versions = "*" +files = [ + {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, + {file = "pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae"}, +] + +[[package]] +name = "pytest" +version = "7.4.0" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.11.1" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-mock-3.11.1.tar.gz", hash = "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f"}, + {file = "pytest_mock-3.11.1-py3-none-any.whl", hash = "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "pytest-xdist" +version = "3.3.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, + {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, +] + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.2.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "pyupgrade" +version = "3.10.1" +description = "A tool to automatically upgrade syntax for newer versions." +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "pyupgrade-3.10.1-py2.py3-none-any.whl", hash = "sha256:f565b4d26daa46ed522e98746834e77e444269103f8bc04413d77dad95169a24"}, + {file = "pyupgrade-3.10.1.tar.gz", hash = "sha256:1d8d138c2ccdd3c42b1419230ae036d5607dc69465a26feacc069642fc8d1b90"}, +] + +[package.dependencies] +tokenize-rt = ">=5.2.0" + +[[package]] +name = "rich" +version = "13.5.2" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, + {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "textual" +version = "0.27.0" +description = "Modern Text User Interface framework" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "textual-0.27.0-py3-none-any.whl", hash = "sha256:dc45eaf7da330686c56d6f76f59d05fd216ce6aad90fa44ee269881efc622151"}, + {file = "textual-0.27.0.tar.gz", hash = "sha256:8bdcb09dc35a706ef939b1276ccfdec10eaaee6147b41cb7587cf33298a8dd33"}, +] + +[package.dependencies] +importlib-metadata = ">=4.11.3" +markdown-it-py = {version = ">=2.1.0,<3.0.0", extras = ["linkify", "plugins"]} +rich = ">=13.3.3" +typing-extensions = ">=4.4.0,<5.0.0" + +[package.extras] +dev = ["aiohttp (>=3.8.1)", "click (>=8.1.2)", "msgpack (>=1.0.3)"] + +[[package]] +name = "tokenize-rt" +version = "5.2.0" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +optional = false +python-versions = ">=3.8" +files = [ + {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"}, + {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"}, +] + +[[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 = "toolz" +version = "0.12.0" +description = "List processing tools and functional utilities" +optional = false +python-versions = ">=3.5" +files = [ + {file = "toolz-0.12.0-py3-none-any.whl", hash = "sha256:2059bd4148deb1884bb0eb770a3cde70e7f954cfbbdc2285f1f2de01fd21eb6f"}, + {file = "toolz-0.12.0.tar.gz", hash = "sha256:88c570861c440ee3f2f6037c4654613228ff40c93a6c25e0eba70d17282c6194"}, +] + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "uc-micro-py" +version = "1.0.2" +description = "Micro subset of unicode data files for linkify-it-py projects." +optional = false +python-versions = ">=3.7" +files = [ + {file = "uc-micro-py-1.0.2.tar.gz", hash = "sha256:30ae2ac9c49f39ac6dce743bd187fcd2b574b16ca095fa74cd9396795c954c54"}, + {file = "uc_micro_py-1.0.2-py3-none-any.whl", hash = "sha256:8c9110c309db9d9e87302e2f4ad2c3152770930d88ab385cd544e7a7e75f3de0"}, +] + +[package.extras] +test = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "wcwidth" +version = "0.2.6" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] + +[[package]] +name = "zipp" +version = "3.16.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.16.2-py3-none-any.whl", hash = "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0"}, + {file = "zipp-3.16.2.tar.gz", hash = "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "6c0e5af7322663a42ee17a19fc09989375d2177e743c67852a2a75e17fb2755c" diff --git a/kmultiversx/pyproject.toml b/kmultiversx/pyproject.toml new file mode 100644 index 00000000..d013b6d5 --- /dev/null +++ b/kmultiversx/pyproject.toml @@ -0,0 +1,57 @@ +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "kmultiversx" +version = "0.1.0" +description = "Python tools for Elrond semantics" +authors = [ + "Runtime Verification, Inc. ", +] + +[tool.poetry.dependencies] +python = "^3.10" +pykwasm = {path = "../deps/wasm-semantics/pykwasm"} +pycryptodomex = "^3.18.0" +hypothesis = "^6.82.6" + +[tool.poetry.group.dev.dependencies] +autoflake = "*" +black = "*" +flake8 = "*" +flake8-bugbear = "*" +flake8-comprehensions = "*" +flake8-quotes = "*" +flake8-type-checking = "*" +isort = "*" +mypy = "*" +pep8-naming = "*" +pytest = "*" +pytest-cov = "*" +pytest-mock = "*" +pytest-xdist = "*" +pyupgrade = "*" + +[tool.isort] +profile = "black" +line_length = 120 + +[tool.autoflake] +recursive = true +expand-star-imports = true +remove-all-unused-imports = true +ignore-init-module-imports = true +remove-duplicate-keys = true +remove-unused-variables = true + +[tool.black] +line-length = 120 +skip-string-normalization = true + +[tool.mypy] +disallow_untyped_defs = true + +[tool.poetry.scripts] +mandos = "kmultiversx.scenario:run_tests" +foundry = "kmultiversx.foundry:main" diff --git a/kmultiversx/src/kmultiversx/__init__.py b/kmultiversx/src/kmultiversx/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/run_foundry.py b/kmultiversx/src/kmultiversx/foundry.py similarity index 83% rename from run_foundry.py rename to kmultiversx/src/kmultiversx/foundry.py index 561b8409..216c21f4 100644 --- a/run_foundry.py +++ b/kmultiversx/src/kmultiversx/foundry.py @@ -1,19 +1,42 @@ +from __future__ import annotations + import argparse import glob +import json +import sys from os.path import join -from typing import Iterable, Mapping - -from hypothesis import given, settings, Verbosity -from hypothesis.strategies import SearchStrategy, integers, tuples -from pyk.prelude.utils import token -from pyk.prelude.collections import list_of, map_of -from pyk.kast.inner import KSort, KVariable -from pyk.cterm import CTerm, build_claim, KClaim -from pyk.utils import ensure_dir_path +from pathlib import Path +from typing import TYPE_CHECKING, Iterable, Mapping, cast + +from hypothesis import Verbosity, given, settings +from hypothesis.strategies import integers, tuples +from pyk.cli.utils import dir_path +from pyk.cterm import CTerm, build_claim +from pyk.kast.inner import KApply, KSequence, KSort, KToken, KVariable, Subst +from pyk.ktool.krun import KRun +from pyk.prelude.collections import list_of, map_of, set_of from pyk.prelude.kint import leInt from pyk.prelude.ml import mlEqualsTrue - -from run_elrond_tests import * +from pyk.prelude.utils import token +from pyk.utils import ensure_dir_path +from pykwasm import wasm2kast +from pykwasm.kwasm_ast import KInt + +from kmultiversx.scenario import ( + KList, + KMapBytesToBytes, + KWasmString, + ListBytes, + get_steps_sc_call, + mandos_argument_to_kbytes, + split_config_from, + wrapBytes, +) + +if TYPE_CHECKING: + from hypothesis.strategies import SearchStrategy + from pyk.cterm import KClaim + from pyk.kast.inner import KInner INPUT_FILE_NAME = 'foundry.json' TEST_PREFIX = 'test_' @@ -22,14 +45,14 @@ TEST_SC_ADDR = 'sc:k-test' REC_LIMIT = 4000 -sys.setrecursionlimit(REC_LIMIT) + def load_input_json(test_dir: str) -> dict: try: with open(join(test_dir, INPUT_FILE_NAME), 'r') as f: return json.load(f) - except FileNotFoundError as e: - raise FileNotFoundError('"{INPUT_FILE_NAME}" not found in "{test_dir}"') + except FileNotFoundError: + raise FileNotFoundError(f'{INPUT_FILE_NAME!r} not found in "{test_dir!r}"') from None def load_wasm(filename: str) -> KInner: @@ -47,9 +70,7 @@ def find_test_wasm_path(test_dir: str) -> str: def load_contract_wasms(contract_wasm_paths: Iterable[str]) -> dict[bytes, KInner]: - contract_wasm_modules = { - bytes(f, 'ascii'): load_wasm(f) for f in contract_wasm_paths - } + contract_wasm_modules = {bytes(f, 'ascii'): load_wasm(f) for f in contract_wasm_paths} return contract_wasm_modules @@ -58,9 +79,7 @@ def set_exit_code(i: int) -> KInner: return KApply('setExitCode', [KInt(i)]) -def deploy_test( - krun: KRun, test_wasm: KInner, contract_wasms: dict[bytes, KInner] -) -> tuple[KInner, dict[str, KInner]]: +def deploy_test(krun: KRun, test_wasm: KInner, contract_wasms: dict[bytes, KInner]) -> tuple[KInner, dict[str, KInner]]: """ 1. create a main account: 'k' 2. reserve a new address for the test contract: owner = 'k', contract address = 'k-test' @@ -87,23 +106,17 @@ def deploy_test( # deploy the test contract arguments = ListBytes(wrapBytes(token(k)) for k in contract_wasms) gas = token(5000000000000) - deploy_cmd = KApply( - 'deployTx', [k_addr, token(0), test_wasm, arguments, gas, token(0)] - ) + deploy_cmd = KApply('deployTx', [k_addr, token(0), test_wasm, arguments, gas, token(0)]) # initialization steps - init_steps = KSequence( - [set_exit_code(1), init_main_acct, new_address, deploy_cmd, set_exit_code(0)] - ) + init_steps = KSequence([set_exit_code(1), init_main_acct, new_address, deploy_cmd, set_exit_code(0)]) # create an empty config and embed init steps empty_conf = krun.definition.init_config(KSort('GeneratedTopCell')) conf, subst = split_config_from(empty_conf) subst['K_CELL'] = init_steps - subst['WASMSTORE_CELL'] = map_of( - {token(path): mod for path, mod in contract_wasms.items()} - ) + subst['WASMSTORE_CELL'] = map_of({cast('KInner', token(path)): mod for path, mod in contract_wasms.items()}) conf_with_steps = Subst(subst)(conf) _, sym_conf, subst = run_config_and_check_empty(krun, conf_with_steps) @@ -114,15 +127,12 @@ def deploy_test( def run_config(krun: KRun, conf: KInner) -> KInner: - conf_kore = krun.kast_to_kore(conf, sort=KSort('GeneratedTopCell')) + conf_kore = krun.kast_to_kore(conf, sort=KSort('GeneratedTopCell')) res_conf_kore = krun.run_kore_term(conf_kore, expect_rc=0) return krun.kore_to_kast(res_conf_kore) -def run_config_and_check_empty( - krun: KRun, conf: KInner -) -> tuple[KInner, KInner, dict[str, KInner]]: - +def run_config_and_check_empty(krun: KRun, conf: KInner) -> tuple[KInner, KInner, dict[str, KInner]]: final_conf = run_config(krun, conf) sym_conf, subst = split_config_from(final_conf) k_cell = subst['K_CELL'] @@ -134,9 +144,7 @@ def run_config_and_check_empty( return final_conf, sym_conf, subst -def run_test( - krun: KRun, sym_conf: KInner, init_subst: dict[str, KInner], endpoint: str, args: tuple[str, ...] -): +def run_test(krun: KRun, sym_conf: KInner, init_subst: dict[str, KInner], endpoint: str, args: tuple[str, ...]) -> None: step = { 'tx': { 'from': ROOT_ACCT_ADDR, @@ -149,9 +157,7 @@ def run_test( }, 'expect': {'status': '0'}, } - tx_steps = KSequence( - [set_exit_code(1)] + get_steps_sc_call(step) + [set_exit_code(0)] - ) + tx_steps = KSequence([set_exit_code(1)] + get_steps_sc_call(step) + [set_exit_code(0)]) subst = init_subst.copy() subst['K_CELL'] = tx_steps @@ -164,6 +170,7 @@ def run_test( raise RuntimeError(f'Test failed for input input: {args}') from None raise rte + # Test metadata @@ -211,14 +218,9 @@ def arg_types_to_strategy(types: Iterable[str]) -> SearchStrategy[tuple[str, ... def test_with_hypothesis( - krun: KRun, - sym_conf: KInner, - init_subst: dict[str, KInner], - endpoint: str, - arg_types: Iterable[str] + krun: KRun, sym_conf: KInner, init_subst: dict[str, KInner], endpoint: str, arg_types: Iterable[str] ) -> None: - - def test(args: tuple[str,...]): + def test(args: tuple[str, ...]) -> None: # set the recursion limit every time because hypothesis changes it if sys.getrecursionlimit() < REC_LIMIT: sys.setrecursionlimit(REC_LIMIT) @@ -243,9 +245,8 @@ def run_concrete( sym_conf: KInner, init_subst: dict[str, KInner], ) -> None: - for endpoint, arg_types in test_endpoints.items(): - print(f'Testing "{endpoint}"') + print(f'Testing {endpoint !r}') test_with_hypothesis(krun, sym_conf, init_subst, endpoint, arg_types) @@ -278,7 +279,6 @@ def generate_claim( sym_conf: KInner, init_subst: dict[str, KInner], ) -> KClaim: - root_acc = mandos_argument_to_kbytes(ROOT_ACCT_ADDR) test_sc = mandos_argument_to_kbytes(TEST_SC_ADDR) vars, ctrs = make_vars_and_constraints(arg_types) @@ -320,7 +320,6 @@ def generate_claim( def lhs_subst(init_subst: dict[str, KInner], steps: KInner) -> dict[str, KInner]: - subst = { 'K_CELL': steps, 'CHECKEDACCOUNTS_CELL': set_of(()), @@ -354,7 +353,6 @@ def lhs_subst(init_subst: dict[str, KInner], steps: KInner) -> dict[str, KInner] def rhs_subst(init_subst: dict[str, KInner]) -> dict[str, KInner]: - subst = { 'K_CELL': KSequence(), 'CHECKEDACCOUNTS_CELL': set_of(()), @@ -389,10 +387,7 @@ def var_to_bytes(var: KVariable) -> KInner: raise TypeError(f'Cannot convert sort {sort} to Bytes') -def make_vars_and_constraints( - types: tuple[str, ...] -) -> tuple[tuple[KVariable, ...], tuple[KInner, ...]]: - +def make_vars_and_constraints(types: tuple[str, ...]) -> tuple[tuple[KVariable, ...], tuple[KInner, ...]]: vars: tuple[KVariable, ...] = () ctrs: tuple[KInner, ...] = () for i, typ in enumerate(types): @@ -404,9 +399,9 @@ def make_vars_and_constraints( def make_var_and_constraints(id: str, typ: str) -> tuple[KVariable, tuple[KInner, ...]]: - ''' + """ Create a K variable and constraints from a type - ''' + """ sort = type_to_sort(typ) var = KVariable(id, sort) @@ -434,13 +429,18 @@ def type_to_constraint(typ: str, var: KVariable) -> tuple[KInner, ...]: # Main Script -def main(): +def main() -> None: sys.setrecursionlimit(REC_LIMIT) - + parser = argparse.ArgumentParser(description='Symbolic testing for MultiversX contracts') parser.add_argument( - '-d', '--directory', required=True, help='path to the test contract' + '--definition-dir', + default=None, + dest='definition_dir', + type=dir_path, + help='Path to Foundry LLVM definition to use.', ) + parser.add_argument('-d', '--directory', required=True, help='path to the test contract') parser.add_argument( '--gen-claims', dest='gen_claims', @@ -468,7 +468,7 @@ def main(): wasm_paths = (join(test_dir, p) for p in input_json['contract_paths']) contract_wasms = load_contract_wasms(wasm_paths) - krun = KRun(Path('.build/defn/llvm/foundry-kompiled')) + krun = KRun(args.definition_dir) print('Initializing the test...') sym_conf, init_subst = deploy_test(krun, test_wasm, contract_wasms) @@ -487,7 +487,3 @@ def main(): generate_claims(krun, test_endpoints, sym_conf, init_subst, output_dir) else: run_concrete(krun, test_endpoints, sym_conf, init_subst) - - -if __name__ == '__main__': - main() diff --git a/kmultiversx/src/kmultiversx/py.typed b/kmultiversx/src/kmultiversx/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/run_elrond_tests.py b/kmultiversx/src/kmultiversx/scenario.py similarity index 66% rename from run_elrond_tests.py rename to kmultiversx/src/kmultiversx/scenario.py index 9a233a88..cb72d2fa 100755 --- a/run_elrond_tests.py +++ b/kmultiversx/src/kmultiversx/scenario.py @@ -2,80 +2,94 @@ import argparse import json -from pathlib import Path -from pyk.kast.inner import KSequence, KInner, KToken, KApply, Subst, KSort -from pyk.ktool.krun import KRun -from pyk.kast.manip import split_config_from -from pyk.prelude.collections import set_of +import os import resource import subprocess import sys -from Cryptodome.Hash import keccak import tempfile -import os -import wasm2kast -from kwasm_ast import KString, KInt, KBytes +from typing import Iterable, Optional, TypeVar + +from Cryptodome.Hash import keccak +from pyk.cli.utils import dir_path +from pyk.kast.inner import KApply, KInner, KSequence, KSort, KToken, Subst +from pyk.kast.manip import split_config_from +from pyk.ktool.krun import KRun +from pyk.prelude.collections import set_of +from pykwasm import wasm2kast +from pykwasm.kwasm_ast import KBytes, KInt, KString -def flatten(l): +T = TypeVar('T') + + +def flatten(l: list[list[T]]) -> list[T]: return [item for sublist in l for item in sublist] -def wrapBytes(bs: KInner): + +def wrapBytes(bs: KToken) -> KInner: # noqa: N802 assert bs.sort == KSort('Bytes') return KApply('wrapBytes', [bs]) -def KWasmString(value): + +def KWasmString(value: str) -> KToken: # noqa: N802 return KToken('"%s"' % value, 'WasmStringToken') -def KMap(kitem_pairs, empty_map:str=".Map", map_item:str="_|->_", map_concat:str="_Map_"): + +def KMap( # noqa: N802 + kitem_pairs: list[tuple[KInner, KInner]], + empty_map: str = '.Map', + map_item: str = '_|->_', + map_concat: str = '_Map_', +) -> KInner: """Takes a list of pairs of KItems and produces a Map with them as keys and values.""" if len(kitem_pairs) == 0: return KApply(empty_map, []) ((k, v), tail) = (kitem_pairs[0], kitem_pairs[1:]) res = KApply(map_item, [k, v]) - for (k, v) in tail: + for k, v in tail: new_item = KApply(map_item, [k, v]) res = KApply(map_concat, [res, new_item]) return res -def KMapBytesToBytes(kitem_pairs): - return KMap( - kitem_pairs, - empty_map=".MapBytesToBytes", - map_item="_Bytes2Bytes|->_", - map_concat="_MapBytesToBytes_") -def KList(items, list_item:str="ListItem", empty:str=".List", concat:str="_List_"): - list_items = list(map(lambda x: KApply(list_item, [x]), items)) - def KList_aux(lis): +def KMapBytesToBytes(kitem_pairs: list[tuple[KInner, KInner]]) -> KInner: # noqa: N802 + return KMap(kitem_pairs, empty_map='.MapBytesToBytes', map_item='_Bytes2Bytes|->_', map_concat='_MapBytesToBytes_') + + +def KList( # noqa: N802 + items: Iterable[KInner], list_item: str = 'ListItem', empty: str = '.List', concat: str = '_List_' +) -> KInner: + list_items = [KApply(list_item, [x]) for x in items] + + def KList_aux(lis: list[KApply]) -> KInner: # noqa: N802 if lis == []: return KApply(empty, []) head = lis[0] tail = KList_aux(lis[1:]) return KApply(concat, [head, tail]) + return KList_aux(list_items) -def ListBytes(items): - return KList(items, empty=".ListBytes", list_item="ListBytesItem", concat="_ListBytes_") -def config_to_kast_term(config): - return { 'format' : 'KAST', 'version': 2, 'term': config.to_dict() } +def ListBytes(items: Iterable[KInner]) -> KInner: # noqa: N802 + return KList(items, empty='.ListBytes', list_item='ListBytesItem', concat='_ListBytes_') -############################### -WASM_definition_main_file = 'mandos' -WASM_definition_llvm_dir = Path('.build/defn/llvm') -WASM_definition_llvm_kompiled_dir = WASM_definition_llvm_dir / (WASM_definition_main_file + '-kompiled') +def config_to_kast_term(config: KInner) -> dict: + return {'format': 'KAST', 'version': 2, 'term': config.to_dict()} -addr_prefix = "address:" -sc_prefix = "sc:" -keccak_prefix = "keccak256:" -u64_prefix = "u64:" -u32_prefix = "u32:" -u16_prefix = "u16:" -u8_prefix = "u8:" -biguint_prefix = "biguint:" -nested_prefix = "nested:" +############################### + +addr_prefix = 'address:' +sc_prefix = 'sc:' +keccak_prefix = 'keccak256:' +u64_prefix = 'u64:' +u32_prefix = 'u32:' +u16_prefix = 'u16:' +u8_prefix = 'u8:' + +biguint_prefix = 'biguint:' +nested_prefix = 'nested:' # number of zero bytes every smart contract address begins with. sc_addr_num_leading_zeros = 8 @@ -88,14 +102,16 @@ def config_to_kast_term(config): sys.setrecursionlimit(1500000000) resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY, resource.RLIM_INFINITY)) -def mandos_int_to_kint(mandos_int: str): + +def mandos_int_to_kint(mandos_int: str) -> KToken: if mandos_int[0:2] == '0x': return KInt(int(mandos_int, 16)) unseparated_int = mandos_int.replace(',', '') parsed_int = int(unseparated_int) return KInt(parsed_int) -def mandos_argument_to_bytes(arg): + +def mandos_argument_to_bytes(arg: str | list | dict) -> bytes: if isinstance(arg, str): return mandos_string_to_bytes(arg) @@ -113,8 +129,9 @@ def mandos_argument_to_bytes(arg): raise ValueError(f'Argument type not yet supported: {arg}') -def mandos_string_to_bytes(raw_str: str): - if raw_str == "": + +def mandos_string_to_bytes(raw_str: str) -> bytes: + if raw_str == '': return bytes() if '|' in raw_str: @@ -124,9 +141,9 @@ def mandos_string_to_bytes(raw_str: str): bs += bytearray(mandos_argument_to_bytes(s)) return bytes(bs) - if raw_str == "false": + if raw_str == 'false': return bytes() - if raw_str == "true": + if raw_str == 'true': return bytes([1]) # string prefix @@ -137,17 +154,17 @@ def mandos_string_to_bytes(raw_str: str): # address if raw_str.startswith(addr_prefix): - addr_arg = raw_str[len(addr_prefix):] + addr_arg = raw_str[len(addr_prefix) :] return address_expression(addr_arg) # smart contract address if raw_str.startswith(sc_prefix): - addr_arg = raw_str[len(sc_prefix):] + addr_arg = raw_str[len(sc_prefix) :] return sc_expression(addr_arg) # keccak256 if raw_str.startswith(keccak_prefix): - input_bytes = mandos_string_to_bytes(raw_str[len(keccak_prefix):]) + input_bytes = mandos_string_to_bytes(raw_str[len(keccak_prefix) :]) k = keccak.new(digest_bits=256) k.update(input_bytes) return bytes.fromhex(k.hexdigest()) @@ -155,20 +172,20 @@ def mandos_string_to_bytes(raw_str: str): # biguint if raw_str.startswith(biguint_prefix): bs = bytearray() - num_int, num_len = convert_string_to_uint(raw_str[len(biguint_prefix):]) + num_int, num_len = convert_string_to_uint(raw_str[len(biguint_prefix) :]) bs += bytearray(num_len.to_bytes(4, 'big')) bs += bytearray(num_int.to_bytes(num_len, 'big')) return bytes(bs) # fixed width number if raw_str.startswith(u64_prefix): - return mandos_interpret_as_uint_fixedwidth(raw_str[len(u64_prefix):], 8) + return mandos_interpret_as_uint_fixedwidth(raw_str[len(u64_prefix) :], 8) if raw_str.startswith(u32_prefix): - return mandos_interpret_as_uint_fixedwidth(raw_str[len(u32_prefix):], 4) + return mandos_interpret_as_uint_fixedwidth(raw_str[len(u32_prefix) :], 4) if raw_str.startswith(u16_prefix): - return mandos_interpret_as_uint_fixedwidth(raw_str[len(u16_prefix):], 2) + return mandos_interpret_as_uint_fixedwidth(raw_str[len(u16_prefix) :], 2) if raw_str.startswith(u8_prefix): - return mandos_interpret_as_uint_fixedwidth(raw_str[len(u8_prefix):], 1) + return mandos_interpret_as_uint_fixedwidth(raw_str[len(u8_prefix) :], 1) # signed integer if raw_str.startswith('+') or raw_str.startswith('-'): @@ -179,7 +196,7 @@ def mandos_string_to_bytes(raw_str: str): pass if raw_str.startswith(nested_prefix): - return interpret_nested_bytes(raw_str[len(nested_prefix):]) + return interpret_nested_bytes(raw_str[len(nested_prefix) :]) # unsigned integer try: @@ -188,33 +205,39 @@ def mandos_string_to_bytes(raw_str: str): except ValueError: pass - raise ValueError("Argument type not yet supported: %s" % raw_str) + raise ValueError(f'Argument type not yet supported: {raw_str}') -def interpret_nested_bytes(raw_str): + +def interpret_nested_bytes(raw_str: str) -> bytes: nested_bytes = mandos_string_to_bytes(raw_str) length_bytes = len(nested_bytes).to_bytes(4, 'big') return length_bytes + nested_bytes + def address_expression(addr_arg: str) -> bytes: return create_address_optional_shard_id(addr_arg, 0) + def create_address_optional_shard_id(input: str, num_leading_zeros: int) -> bytes: # TODO implement addresses with optional shard ID: https://github.com/multiversx/mx-chain-scenario-go/blob/3d0b8aea51a94fe640bf1c62a78dd5b4abbad459/expression/interpreter/functions.go#L52 - zero_padded = "\0" * num_leading_zeros + input + zero_padded = '\0' * num_leading_zeros + input padded_addr = zero_padded.ljust(32, '_') padded_addr_bytes = bytes(padded_addr[:32], 'ascii') return padded_addr_bytes + def sc_expression(input: str) -> bytes: addr = create_address_optional_shard_id(input, sc_addr_reserved_prefix_len) # TODO insert VM type: https://github.com/multiversx/mx-chain-scenario-go/blob/3d0b8aea51a94fe640bf1c62a78dd5b4abbad459/expression/interpreter/functions.go#L78 return addr -def mandos_interpret_as_uint_fixedwidth(raw_str: str, width: int): + +def mandos_interpret_as_uint_fixedwidth(raw_str: str, width: int) -> bytes: num_int, _ = convert_string_to_uint(raw_str) return num_int.to_bytes(width, byteorder='big') -def convert_string_to_uint(raw_str: str): + +def convert_string_to_uint(raw_str: str) -> tuple[int, int]: num_str = raw_str.replace('_', '') num_str = num_str.replace(',', '') @@ -239,44 +262,52 @@ def convert_string_to_uint(raw_str: str): return (num_int, num_len) num_int = int(num_str) - if (num_int < 0): - raise ValueError("Negative number not allowed in this context: %s" % raw_str) + if num_int < 0: + raise ValueError(f'Negative number not allowed in this context: {raw_str}') num_len = (num_int.bit_length() + 7) // 8 return (num_int, num_len) -def convert_string_to_sint(raw_str: str): + +def convert_string_to_sint(raw_str: str) -> tuple[int, int]: num_int, _ = convert_string_to_uint(raw_str[1:]) if raw_str.startswith('-'): num_int = -num_int num_len = (8 + (num_int + (num_int < 0)).bit_length()) // 8 return (num_int, num_len) -def mandos_argument_to_kbytes(argument: str): + +def mandos_argument_to_kbytes(argument: str) -> KToken: bs = mandos_argument_to_bytes(argument) return KBytes(bs) -def mandos_arguments_to_klist(arguments: list): - tokenized = list(map(lambda x: mandos_argument_to_kbytes(x), arguments)) + +def mandos_arguments_to_klist(arguments: list[str]) -> KInner: + tokenized = [mandos_argument_to_kbytes(x) for x in arguments] wrapped = list(map(wrapBytes, tokenized)) return ListBytes(wrapped) -def mandos_to_set_account(address, sections, filename, output_dir): - """Creates a K account cell from a Mandos account description. """ + +def mandos_to_set_account(address: str, sections: dict, filename: str, output_dir: str) -> list[KApply]: + """Creates a K account cell from a Mandos account description.""" address_value = mandos_argument_to_kbytes(address) - nonce_value = mandos_int_to_kint(sections.get('nonce', '0')) + nonce_value = mandos_int_to_kint(sections.get('nonce', '0')) balance_value = mandos_int_to_kint(sections.get('balance', '0')) - owner_value = mandos_argument_to_kbytes(sections.get('owner', '')) - code_value = KApply(".Code", []) + owner_value = mandos_argument_to_kbytes(sections.get('owner', '')) + code_value: KInner = KApply('.Code', []) if 'code' in sections: code_path = get_contract_code(sections['code'], filename) if code_path is not None: code_value = file_to_module_decl(code_path, output_dir) - storage_pairs = [ (mandos_argument_to_kbytes(k), mandos_argument_to_kbytes(v)) for (k, v) in sections.get('storage', {}).items() ] - storage_pairs = [ (wrapBytes(k), wrapBytes(v)) for (k, v) in storage_pairs ] - storage_value = KMapBytesToBytes(storage_pairs) + storage_pairs = [ + (mandos_argument_to_kbytes(k), mandos_argument_to_kbytes(v)) for (k, v) in sections.get('storage', {}).items() + ] + wrapped_pairs = [(wrapBytes(k), wrapBytes(v)) for (k, v) in storage_pairs] + storage_value = KMapBytesToBytes(wrapped_pairs) - set_account_steps = [KApply('setAccount', [address_value, nonce_value, balance_value, code_value, owner_value, storage_value])] + set_account_steps = [ + KApply('setAccount', [address_value, nonce_value, balance_value, code_value, owner_value, storage_value]) + ] if 'esdt' in sections: for k, v in sections['esdt'].items(): @@ -287,16 +318,17 @@ def mandos_to_set_account(address, sections, filename, output_dir): return set_account_steps -# ESDT value is either an integer (compact) or a dictionary (full) -def mandos_to_esdt_value(v): - try: + +# ESDT value is either an integer (compact) or a dictionary (full) +def mandos_to_esdt_value(v: str | dict) -> KToken: + if isinstance(v, str): return mandos_int_to_kint(v) - except TypeError: - # TODO properly parse 'instances' - return mandos_int_to_kint(v['instances'][0]['balance']) + # TODO properly parse 'instances' + return mandos_int_to_kint(v['instances'][0]['balance']) -def mandos_to_check_account(address, sections, filename): - k_steps = [] + +def mandos_to_check_account(address: str, sections: dict, filename: str) -> list: + k_steps: list[KInner] = [] address_value = mandos_argument_to_kbytes(address) if ('nonce' in sections) and (sections['nonce'] != '*'): nonce_value = mandos_int_to_kint(sections['nonce']) @@ -305,52 +337,55 @@ def mandos_to_check_account(address, sections, filename): balance_value = mandos_int_to_kint(sections['balance']) k_steps.append(KApply('checkAccountBalance', [address_value, balance_value])) if ('storage' in sections) and (sections['storage'] != '*'): + # TODO move storage map creation to a function and reuse in mandos_to_set_account storage_pairs = [] - for (k, v) in sections['storage'].items(): + for k, v in sections['storage'].items(): k_bytes = mandos_argument_to_kbytes(k) v_bytes = mandos_argument_to_kbytes(v) storage_pairs.append((k_bytes, v_bytes)) - storage_pairs = [ (wrapBytes(k), wrapBytes(v)) for (k, v) in storage_pairs ] - storage_value = KMapBytesToBytes(storage_pairs) + wrapped_pairs = [(wrapBytes(k), wrapBytes(v)) for (k, v) in storage_pairs] + storage_value = KMapBytesToBytes(wrapped_pairs) k_steps.append(KApply('checkAccountStorage', [address_value, storage_value])) if ('code' in sections) and (sections['code'] != '*'): code_path = get_contract_code(sections['code'], filename) if code_path is None: - code_path = "" - code_path = KString(code_path) - k_steps.append(KApply('checkAccountCode', [address_value, code_path])) + code_path = '' + k_code_path = KString(code_path) + k_steps.append(KApply('checkAccountCode', [address_value, k_code_path])) k_steps.append(KApply('checkedAccount', [address_value])) return k_steps -def mandos_to_deploy_tx(tx, filename, output_dir): + +def mandos_to_deploy_tx(tx: dict, filename: str, output_dir: str) -> KInner: sender = mandos_argument_to_kbytes(tx['from']) - value = mandos_int_to_kint(getEgldValue(tx)) + value = mandos_int_to_kint(get_egld_value(tx)) arguments = mandos_arguments_to_klist(tx['arguments']) - gasLimit = mandos_int_to_kint(tx['gasLimit']) - gasPrice = mandos_int_to_kint(tx['gasPrice']) + gas_limit = mandos_int_to_kint(tx['gasLimit']) + gas_price = mandos_int_to_kint(tx['gasPrice']) code = get_contract_code(tx['contractCode'], filename) + assert isinstance(code, str) module = file_to_module_decl(code, output_dir) - deployTx = KApply('deployTx', [sender, value, module, arguments, gasLimit, gasPrice]) - return deployTx + return KApply('deployTx', [sender, value, module, arguments, gas_limit, gas_price]) -def mandos_to_call_tx(tx): + +def mandos_to_call_tx(tx: dict) -> KInner: sender = mandos_argument_to_kbytes(tx['from']) to = mandos_argument_to_kbytes(tx['to']) - value = mandos_int_to_kint(getEgldValue(tx)) + value = mandos_int_to_kint(get_egld_value(tx)) esdt_value = mandos_esdt_to_klist(tx.get('esdtValue', [])) function = KWasmString(tx['function']) arguments = mandos_arguments_to_klist(tx['arguments']) - gasLimit = mandos_int_to_kint(tx['gasLimit']) - gasPrice = mandos_int_to_kint(tx['gasPrice']) + gas_limit = mandos_int_to_kint(tx['gasLimit']) + gas_price = mandos_int_to_kint(tx['gasPrice']) + + return KApply('callTx', [sender, to, value, esdt_value, function, arguments, gas_limit, gas_price]) - callTx = KApply('callTx', [sender, to, value, esdt_value, function, arguments, gasLimit, gasPrice]) - return callTx -def mandos_esdt_to_klist(esdt_values): - def esdt(esdt_value): +def mandos_esdt_to_klist(esdt_values: list[dict]) -> KInner: + def esdt(esdt_value: dict) -> KApply: tok_id = mandos_argument_to_kbytes(esdt_value['tokenIdentifier']) value = mandos_int_to_kint(esdt_value['value']) nonce = mandos_int_to_kint(esdt_value.get('nonce', '0')) @@ -359,54 +394,57 @@ def esdt(esdt_value): return KList(esdt(i) for i in esdt_values) -def mandos_to_transfer_tx(tx): +def mandos_to_transfer_tx(tx: dict) -> KInner: sender = mandos_argument_to_kbytes(tx['from']) to = mandos_argument_to_kbytes(tx['to']) - value = mandos_int_to_kint(getEgldValue(tx)) - - transferTx = KApply('transferTx', [sender, to, value]) - return transferTx + value = mandos_int_to_kint(get_egld_value(tx)) + + return KApply('transferTx', [sender, to, value]) -def mandos_to_validator_reward_tx(tx): + +def mandos_to_validator_reward_tx(tx: dict) -> KApply: to = mandos_argument_to_kbytes(tx['to']) - value = mandos_int_to_kint(getEgldValue(tx)) - rewardTx = KApply('validatorRewardTx', [to, value]) - return rewardTx + value = mandos_int_to_kint(get_egld_value(tx)) + + return KApply('validatorRewardTx', [to, value]) + -def getEgldValue(tx): +def get_egld_value(tx: dict) -> str: # backwards compatibility if 'value' in tx: return tx['value'] - return tx.get('egldValue', "0") + return tx.get('egldValue', '0') + # TODO: implement checkExpect gas, refund -def mandos_to_expect(expect): +STATUS_CODES = { + 0: 'OK', + 1: 'FunctionNotFound', + 2: 'FunctionWrongSignature', + 3: 'ContractNotFound', + 4: 'UserError', + 5: 'OutOfGas', + 6: 'AccountCollision', + 7: 'OutOfFunds', + 8: 'CallStackOverFlow', + 9: 'ContractInvalid', + 10: 'ExecutionFailed', + 11: 'UpgradeFailed', + 12: 'SimulateFailed', +} + + +def mandos_to_expect(expect: dict) -> list: k_steps = [] - def int_to_kreturncode(status: str): - if status == "": + def int_to_kreturncode(status: str) -> KApply: + if status == '': return KApply('OK', []) status_int, _ = convert_string_to_uint(status) - STATUS_CODES = { - 0: 'OK', - 1: 'FunctionNotFound', - 2: 'FunctionWrongSignature', - 3: 'ContractNotFound', - 4: 'UserError', - 5: 'OutOfGas', - 6: 'AccountCollision', - 7: 'OutOfFunds', - 8: 'CallStackOverFlow', - 9: 'ContractInvalid', - 10: 'ExecutionFailed', - 11: 'UpgradeFailed', - 12: 'SimulateFailed', - } - if status_int in STATUS_CODES: return KApply(STATUS_CODES[status_int], []) - raise ValueError("Status code %s not supported" % status) + raise ValueError(f'Status code {status} not supported') if ('out' in expect) and (expect['out'] != '*'): expect_out = mandos_arguments_to_klist(expect['out']) @@ -422,12 +460,13 @@ def int_to_kreturncode(status: str): identifier = mandos_argument_to_kbytes(log['endpoint']) topics = mandos_arguments_to_klist(log['topics']) data = mandos_argument_to_kbytes(log['data']) - logEntry = KApply('logEntry', [address, identifier, topics, data]) - logs.append(logEntry) + log_entry = KApply('logEntry', [address, identifier, topics, data]) + logs.append(log_entry) k_steps.append(KApply('checkExpectLogs', [KList(logs)])) return k_steps -def mandos_to_block_info(block_info): + +def mandos_to_block_info(block_info: dict) -> list: block_infos = [] if 'blockTimestamp' in block_info: block_infos += [KApply('blockTimestamp', [mandos_int_to_kint(block_info['blockTimestamp'])])] @@ -441,52 +480,59 @@ def mandos_to_block_info(block_info): block_infos += [KApply('blockRandomSeed', [mandos_argument_to_kbytes(block_info['blockRandomSeed'])])] return block_infos -def register(with_name: str): + +def register(with_name: str) -> KInner: return KApply('register', [KString(with_name)]) -def file_to_module_decl(filename: str, output_dir): + +def file_to_module_decl(filename: str, output_dir: str) -> KInner: if filename[-5:] == '.wasm': return wasm_file_to_module_decl(filename) if filename[-5:] == '.wast' or filename[-4:] == '.wat': return wat_file_to_module_decl(filename, output_dir) - raise ValueError('Filetype not yet supported: %s' % filename) + raise ValueError(f'Filetype not yet supported: {filename}') -def wasm_file_to_module_decl(filename: str): + +def wasm_file_to_module_decl(filename: str) -> KInner: # Check that file exists. with open(filename, 'rb') as f: module = wasm2kast.wasm2kast(f, filename) return module -def wat_file_to_module_decl(filename: str, output_dir): + +def wat_file_to_module_decl(filename: str, output_dir: str) -> KInner: if not os.path.exists(filename): - raise Exception("file %s does not exist" % filename) - + raise Exception(f'file {filename} does not exist') + new_wasm_filename = os.path.join(output_dir, os.path.basename(filename) + '.wasm') try: - wat = subprocess.check_output("wat2wasm %s --output=%s" % (filename, new_wasm_filename), shell=True) + subprocess.check_output(f'wat2wasm {filename} --output={new_wasm_filename}', shell=True) except subprocess.CalledProcessError as e: - print("Failed: %s" % e.cmd) - print("return code: %d" % e.returncode) - print("stdout:") + print('Failed: %s' % e.cmd) + print('return code: %d' % e.returncode) + print('stdout:') print(e.output) - print("stderr:") + print('stderr:') print(e.stderr) raise e return wasm_file_to_module_decl(new_wasm_filename) -def get_external_file_path(test_file, rel_path_to_new_file): + +def get_external_file_path(test_file: str, rel_path_to_new_file: str) -> str: test_file_path = os.path.dirname(test_file) ext_file = os.path.normpath(os.path.join(test_file_path, rel_path_to_new_file)) return ext_file -def get_contract_code(code, filename): + +def get_contract_code(code: str, filename: str) -> Optional[str]: if code[0:5] == 'file:': return get_external_file_path(filename, code[5:]) if code == '': return None raise Exception('Currently only support getting code from file, or empty code.') -def get_steps_sc_deploy(step, filename, output_dir): + +def get_steps_sc_deploy(step: dict, filename: str, output_dir: str) -> list: k_steps = [] tx = mandos_to_deploy_tx(step['tx'], filename, output_dir) k_steps.append(tx) @@ -495,7 +541,8 @@ def get_steps_sc_deploy(step, filename, output_dir): k_steps += expect return k_steps -def get_steps_sc_call(step): + +def get_steps_sc_call(step: dict) -> list: k_steps = [] tx = mandos_to_call_tx(step['tx']) k_steps.append(tx) @@ -504,15 +551,16 @@ def get_steps_sc_call(step): k_steps += expect return k_steps -def mandos_to_query_tx(tx): + +def mandos_to_query_tx(tx: dict) -> KInner: to = mandos_argument_to_kbytes(tx['to']) function = KWasmString(tx['function']) arguments = mandos_arguments_to_klist(tx.get('arguments', [])) - queryTx = KApply('queryTx', [to, function, arguments]) - return queryTx + return KApply('queryTx', [to, function, arguments]) -def get_steps_sc_query(step): + +def get_steps_sc_query(step: dict) -> list: k_steps = [] tx = mandos_to_query_tx(step['tx']) @@ -521,52 +569,60 @@ def get_steps_sc_query(step): if 'expect' in step: expect = mandos_to_expect(step['expect']) k_steps += expect - + return k_steps -def get_steps_transfer(step): + +def get_steps_transfer(step: dict) -> list: tx = mandos_to_transfer_tx(step['tx']) return [KApply('transfer', [tx])] -def get_steps_validator_reward(step): + +def get_steps_validator_reward(step: dict) -> list[KApply]: tx = mandos_to_validator_reward_tx(step['tx']) return [KApply('validatorReward', [tx])] -def get_steps_new_addresses(new_addresses): + +def get_steps_new_addresses(new_addresses: Optional[dict]) -> list[KApply]: if new_addresses is None: return [] - ret = [] + ret: list[KApply] = [] for new_address in new_addresses: creator = mandos_argument_to_kbytes(new_address['creatorAddress']) - nonce = mandos_int_to_kint(new_address['creatorNonce']) - new = mandos_argument_to_kbytes(new_address['newAddress']) + nonce = mandos_int_to_kint(new_address['creatorNonce']) + new = mandos_argument_to_kbytes(new_address['newAddress']) ret.append(KApply('newAddress', [creator, nonce, new])) return ret -def get_steps_set_state(step, filename, output_dir): - k_steps = [] + +def get_steps_set_state(step: dict, filename: str, output_dir: str) -> list[KApply]: + k_steps: list[KApply] = [] if 'accounts' in step: - set_accounts = [ mandos_to_set_account(address, sections, filename, output_dir) for (address, sections) in step['accounts'].items() ] + set_accounts = [ + mandos_to_set_account(address, sections, filename, output_dir) + for (address, sections) in step['accounts'].items() + ] k_steps = k_steps + flatten(set_accounts) if 'newAddresses' in step: new_addresses = get_steps_new_addresses(step['newAddresses']) k_steps = k_steps + new_addresses if 'currentBlockInfo' in step: block_infos = mandos_to_block_info(step['currentBlockInfo']) - set_current_blockInfos = list(map(lambda x: KApply('setCurBlockInfo', [x]), block_infos)) - k_steps = k_steps + set_current_blockInfos + set_current_block_infos = [KApply('setCurBlockInfo', [x]) for x in block_infos] + k_steps = k_steps + set_current_block_infos if 'previousBlockInfo' in step: block_infos = mandos_to_block_info(step['previousBlockInfo']) - set_previous_blockInfos = list(map(lambda x: KApply('setPrevBlockInfo', [x]), block_infos)) - k_steps = k_steps + set_previous_blockInfos + set_previous_block_infos = [KApply('setPrevBlockInfo', [x]) for x in block_infos] + k_steps = k_steps + set_previous_block_infos if k_steps == []: raise Exception('Step not implemented: %s' % step) return k_steps -def get_steps_check_state(step, filename): + +def get_steps_check_state(step: dict, filename: str) -> list: k_steps = [] if 'accounts' in step: - for (address, sections) in step['accounts'].items(): + for address, sections in step['accounts'].items(): if address != '+': k_steps += mandos_to_check_account(address, sections, filename) if not '+' in step['accounts'].keys(): @@ -576,8 +632,8 @@ def get_steps_check_state(step, filename): k_steps.append(KApply('clearCheckedAccounts', [])) return k_steps -def get_steps_as_kseq(filename, output_dir): - global args + +def get_steps_as_kseq(filename: str, output_dir: str, args: argparse.Namespace) -> list: with open(filename, 'r') as f: mandos_test = json.loads(f.read()) if 'name' in mandos_test: @@ -585,7 +641,7 @@ def get_steps_as_kseq(filename, output_dir): print('Reading "%s"' % mandos_test['name']) if 'comment' in mandos_test: if args.verbose: - print('Comment:\n"%s"' % mandos_test['comment']) + print('Comment:"%s"' % mandos_test['comment']) k_steps = [] for step in mandos_test['steps']: @@ -602,7 +658,7 @@ def get_steps_as_kseq(filename, output_dir): elif step['step'] == 'externalSteps': steps_file = get_external_file_path(filename, step['path']) print('Load external: %s' % steps_file) - k_steps = k_steps + get_steps_as_kseq(steps_file, output_dir) + k_steps = k_steps + get_steps_as_kseq(steps_file, output_dir, args) elif step['step'] == 'transfer': k_steps.append((step['step'], get_steps_transfer(step))) elif step['step'] == 'validatorReward': @@ -611,21 +667,27 @@ def get_steps_as_kseq(filename, output_dir): raise Exception('Step %s not implemented yet' % step['step']) return k_steps -def run_test_file(krun: KRun, template_wasm_config, test_file_path, output_dir, cmd_args): - global args + +def run_test_file( + krun: KRun, + template_wasm_config: KInner, + test_file_path: str, + output_dir: str, + cmd_args: argparse.Namespace, +) -> KInner: test_name = os.path.basename(test_file_path) - k_steps = get_steps_as_kseq(test_file_path, output_dir) - final_config = None + k_steps = get_steps_as_kseq(test_file_path, output_dir, cmd_args) + final_config = template_wasm_config if cmd_args.log_level == 'none' or cmd_args.log_level == 'per-file': # Flatten the list of k_steps, just run them all in one go. - k_steps = [ ('full', [ y for (_, x) in k_steps for y in x ]) ] + k_steps = [('full', [y for (_, x) in k_steps for y in x])] (symbolic_config, init_subst) = split_config_from(template_wasm_config) for i in range(len(k_steps)): step_name, curr_step = k_steps[i] - if args.verbose: + if cmd_args.verbose: print('Executing step %s' % step_name) init_subst['K_CELL'] = KSequence(curr_step) @@ -633,66 +695,81 @@ def run_test_file(krun: KRun, template_wasm_config, test_file_path, output_dir, # Run: generate a new JSON as a temporary file, then read that as the new wasm state. if cmd_args.log_level != 'none': - log_intermediate_state(krun, "%s_%d_%s.pre" % (test_name, i, step_name), init_config, output_dir) + log_intermediate_state(krun, '%s_%d_%s.pre' % (test_name, i, step_name), init_config, output_dir) new_config = krun_config(krun, init_config=init_config) final_config = new_config if cmd_args.log_level != 'none': - log_intermediate_state(krun, "%s_%d_%s" % (test_name, i, step_name), final_config, output_dir) + log_intermediate_state(krun, '%s_%d_%s' % (test_name, i, step_name), final_config, output_dir) # Check if the k cell is empty symbolic_config, init_subst = split_config_from(new_config) k_cell = init_subst['K_CELL'] - if not isinstance(k_cell, KSequence) or k_cell.arity != 0: + assert isinstance(k_cell, KSequence) + + if k_cell.arity != 0: raise ValueError(f'k cell not empty, contains a sequence of {k_cell.arity} items.\nSee {output_dir}') return final_config + def krun_config(krun: KRun, init_config: KInner) -> KInner: kore_config = krun.kast_to_kore(init_config, sort=KSort('GeneratedTopCell')) kore_config = krun.run_kore_term(kore_config) return krun.kore_to_kast(kore_config) + # ... Setup Elrond Wasm -def log_intermediate_state(krun: KRun, name, config, output_dir): + +def log_intermediate_state(krun: KRun, name: str, config: KInner, output_dir: str) -> None: with open('%s/%s' % (output_dir, name), 'w') as f: f.write(json.dumps(config_to_kast_term(config))) with open('%s/%s.pretty.k' % (output_dir, name), 'w') as f: pretty = krun.pretty_print(config) f.write(pretty) + # Main Script args = None -def run_tests(): + +def run_tests() -> None: global args - testArgs = argparse.ArgumentParser(description='') - testArgs.add_argument('files', metavar='N', type=str, nargs='+', help='') - testArgs.add_argument('--log-level', choices=['none', 'per-file', 'per-step'], default='per-file') - testArgs.add_argument('--verbose', action='store_true', help='') - args = testArgs.parse_args() + test_args = argparse.ArgumentParser(description='') + test_args.add_argument('files', metavar='N', type=str, nargs='+', help='') + test_args.add_argument('--log-level', choices=['none', 'per-file', 'per-step'], default='per-file') + test_args.add_argument('--verbose', action='store_true', help='') + test_args.add_argument( + '--definition-dir', + default=None, + dest='definition_dir', + type=dir_path, + help='Path to Mandos LLVM definition to use.', + ) + args = test_args.parse_args() tests = args.files - krun = KRun(WASM_definition_llvm_kompiled_dir) - + krun = KRun(args.definition_dir) + with open('src/elrond-runtime.loaded.json', 'r') as f: runtime_json = json.load(f) template_wasm_config = KInner.from_dict(runtime_json['term']) - + _, cells = split_config_from(template_wasm_config) + assert isinstance(cells['K_CELL'], KSequence) assert cells['K_CELL'].arity == 0 for test in tests: if args.verbose: - print("Running test %s" % test) - tmpdir = tempfile.mkdtemp(prefix="mandos_") + print('Running test %s' % test) + tmpdir = tempfile.mkdtemp(prefix='mandos_') if args.verbose: - print("Intermediate test outputs stored in:\n%s" % tmpdir) + print('Intermediate test outputs stored in:\n%s' % tmpdir) - initial_name = "0000_initial_config" + initial_name = '0000_initial_config' with open('%s/%s' % (tmpdir, initial_name), 'w') as f: f.write(json.dumps(config_to_kast_term(template_wasm_config))) @@ -701,6 +778,3 @@ def run_tests(): if args.verbose: print('See %s' % tmpdir) print() - -if __name__ == "__main__": - run_tests() diff --git a/kmultiversx/src/tests/__init__.py b/kmultiversx/src/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kmultiversx/src/tests/integration/__init__.py b/kmultiversx/src/tests/integration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kmultiversx/src/tests/unit/__init__.py b/kmultiversx/src/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b