From d67e62715edd32d3912e31e72c3e50f82aefadda Mon Sep 17 00:00:00 2001 From: zakstucke <44890343+zakstucke@users.noreply.github.com> Date: Thu, 13 Jun 2024 08:09:56 +0200 Subject: [PATCH] Misc (#42) --- .dockerignore | 196 ++++++++ .github/actions/install-rust/action.yml | 37 ++ .github/workflows/release.yml | 33 +- .github/workflows/tests.yml | 36 +- .gitignore | 77 ++-- .pre-commit-config.yaml | 37 +- .zetch.lock | 23 +- bitbazaar.code-workspace | 34 +- dev_scripts/_scr_setup/setup.sh | 2 +- dev_scripts/docs.sh | 2 +- dev_scripts/initial_setup.sh | 276 +++++++---- dev_scripts/pkg.sh | 2 +- dev_scripts/process.sh | 18 +- dev_scripts/py_rust.sh | 8 +- dev_scripts/run.sh | 49 +- dev_scripts/test.sh | 36 +- dev_scripts/utils.sh | 106 ++++- dev_scripts/zj.sh | 28 ++ js/bun.lockb | Bin 252506 -> 52457 bytes js/package-lock.json | 26 +- js/package.json | 13 +- js/tsconfig.json | 3 +- js/tsconfig.zetch.json | 3 +- opencollector.yaml | 12 - opencollector.yaml.zetch | 23 +- py/bitbazaar/__init__.py | 1 + py/bitbazaar/log/_formatting.py | 2 +- py/pdm.lock | 265 ++++++----- py/pyproject.toml | 5 +- py/tests/test_version.py | 8 + py_rust/.config/nextest.toml | 112 +++++ py_rust/Cargo.lock | 417 ++++++++++------- py_rust/Cargo.toml | 10 +- py_rust/dev_requirements.txt | 6 + py_rust/rustfmt.toml | 2 +- py_rust/src/lib.rs | 5 +- py_rust/src/prelude.rs | 8 +- py_rust/tests/test_basic.py | 9 - py_rust/tests/test_version.py | 8 + rust/.config/nextest.toml | 10 +- rust/Cargo.lock | 589 +++++++++++++++++++++--- rust/Cargo.toml | 101 ++-- rust/benches/bench_setup_test.rs | 25 + rust/bitbazaar/lib.rs | 2 + rust/bitbazaar/prelude.rs | 7 +- rust/rustfmt.toml | 3 +- zetch.config.toml | 13 +- 47 files changed, 1903 insertions(+), 785 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/actions/install-rust/action.yml create mode 100755 dev_scripts/zj.sh create mode 100644 py/tests/test_version.py create mode 100644 py_rust/.config/nextest.toml create mode 100644 py_rust/dev_requirements.txt delete mode 100644 py_rust/tests/test_basic.py create mode 100644 py_rust/tests/test_version.py create mode 100644 rust/benches/bench_setup_test.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..68e52332 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,196 @@ +Dockerfile +Dockerfile.* + +# Ignore private folder and any file with private in the name: +**/**/private/ +*private* +# Except for zetch files: +!*private*.zetch.* +!*private*.*.zetch + +**/**/process_data/ +**/**/logs/ + +# Tempate files +.cop.*.yml + +scratch_space.ipynb + +**/**/node_modules/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# Files flagged for ignore: which have .gitignore. inside them: +*.gitignore.* +*_gitignore.* + +# Celery: +celerybeat-schedule.db + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Pdm: +.pdm-python + +# Distribution / packaging +.Python +sds/sworker/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +# leptos build files: +**/**/static/rsite/ +# Compiled css: +**/**/static/css/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +**/**/htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +**/**/coverage/ +nosetests.xml +coverage.xml +*.cover +*.py,cover +**/**/.hypothesis/ +**/**/.pytest_cache/ +**/**/.ruff_cache/ +**/**/cover/ +**/**/.nox/ +*.prof +prof/ +**/**/perf_profiles/ +**/**/test-results/ +**/**/playwright-report/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +**/**/instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# PyBuilder +**/**/.pybuilder/ +**/**/target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +**/**/profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +**/**/__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +**/**/.env +**/**/.venv +**/**/env/ +**/**/venv/ +**/**/ENV/ +**/**/env.bak/ +**/**/venv.bak/ + +# mkdocs local build directory: +**/**/site/ +**/**/docs/js_ref/ +**/**/docs/rust_ref/ +.staticrypt.json + +# Vscode internals +**/**/.vscode/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mypy +**/**/.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +**/**/.pyre/ + +# pytype static type analyzer +**/**/.pytype/ + +# Cython debug symbols +**/**/cython_debug/ + +# Pycharm +**/**/.idea/ + +# Mac stuff +.DS_Store + +**/**/template_test_build/ + +# User diff --git a/.github/actions/install-rust/action.yml b/.github/actions/install-rust/action.yml new file mode 100644 index 00000000..4929c732 --- /dev/null +++ b/.github/actions/install-rust/action.yml @@ -0,0 +1,37 @@ +name: Setup Rust +description: "Installs latest stable rust, and sets up sscache for caching." +inputs: + qa: + description: "Whether things like cargo-hack need installing." + required: false + default: "false" + test: + description: "Whether things like nextest need installing." + required: false + default: "false" +runs: + using: composite + steps: + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.4 + - name: Set Rust caching env vars + shell: bash + run: | + echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV + echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV + # Really this would need to be run as a post task to be of any use (to actually see hits), + # but post in composite action not currently supported: + # https://github.com/actions/runner/issues/1478 + # - name: Run sccache stat for check + # shell: bash + # run: ${SCCACHE_PATH} --show-stats + - name: "Install cargo-hack, used for feature checking in pre-commit." + if: ${{ inputs.qa == 'true' }} + uses: taiki-e/install-action@cargo-hack + - name: Install nextest + if: ${{ inputs.test == 'true' }} + uses: taiki-e/install-action@nextest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4d7c835..d0f44e58 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,7 +54,6 @@ jobs: with: token: ${{ secrets.VERSION_BOT_PAT }} - uses: actions/setup-python@v4 - - uses: ./.github/actions/install-pre-commit - name: Install zetch run: pip install zetch @@ -69,9 +68,9 @@ jobs: - name: Update Python version if: ${{ inputs.py_release }} run: | - zetch put zetch.config.toml context.static.PY_VERSION.value ${{ inputs.py_version }} + zetch put zetch.config.toml context.static.PY_VERSION ${{ inputs.py_version }} - # Js project + # Js project - name: Install Bun, no npm should be needed if: ${{ inputs.js_release }} uses: oven-sh/setup-bun@v1 @@ -85,28 +84,22 @@ jobs: - name: Update JS version if: ${{ inputs.js_release }} run: | - zetch put zetch.config.toml context.static.JS_VERSION.value ${{ inputs.js_version }} - - - uses: dtolnay/rust-toolchain@stable - if: ${{ inputs.py_rust_release }} || ${{ inputs.rust_release }} - with: - components: rustfmt, clippy + zetch put zetch.config.toml context.static.JS_VERSION ${{ inputs.js_version }} - - name: Install cargo-hack, used for feature checking in pre-commit. + - uses: ./.github/actions/install-rust if: ${{ inputs.py_rust_release }} || ${{ inputs.rust_release }} - uses: taiki-e/install-action@cargo-hack - - name: Update Rust-backed Python version if: ${{ inputs.py_rust_release }} run: | - zetch put zetch.config.toml context.static.PY_RUST_VERSION.value ${{ inputs.py_rust_version }} + zetch put zetch.config.toml context.static.PY_RUST_VERSION ${{ inputs.py_rust_version }} - name: Update Rust version if: ${{ inputs.rust_release }} run: | - zetch put zetch.config.toml context.static.RUST_VERSION.value ${{ inputs.rust_version }} + zetch put zetch.config.toml context.static.RUST_VERSION ${{ inputs.rust_version }} + - uses: ./.github/actions/install-pre-commit - name: add and format added files with pre-commit # Running on staged change only as that's all that's needed, || true as don't want it to fail, just modify run: | @@ -160,13 +153,7 @@ jobs: with: node-version: '20' - - name: Install rust toolchain - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - workspaces: ./rust -> target - cache-on-failure: 'true' - cache-all-crates: 'true' + - uses: ./.github/actions/install-rust - name: Build docs run: | @@ -334,9 +321,7 @@ jobs: ref: ${{ needs.commit_versions.outputs.new-sha }} - - name: Install rust toolchain - uses: dtolnay/rust-toolchain@stable - + - uses: ./.github/actions/install-rust - name: upload to Crates.io run: | cd rust diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4fbba383..1c697e63 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,7 +38,6 @@ jobs: with: python-version: "3.12" cache: pip - - uses: ./.github/actions/install-pre-commit # Python project - name: Set up PDM @@ -62,12 +61,11 @@ jobs: cd ./js bun install - - uses: dtolnay/rust-toolchain@stable + - uses: ./.github/actions/install-rust with: - components: rustfmt, clippy - - name: "Install cargo-hack, used for feature checking in pre-commit." - uses: taiki-e/install-action@cargo-hack + qa: true + - uses: ./.github/actions/install-pre-commit - name: Run QA run: | ./dev_scripts/test.sh qa @@ -96,13 +94,9 @@ jobs: with: node-version: "20" - - name: Install rust toolchain - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 + - uses: ./.github/actions/install-rust with: - workspaces: "./rust -> target" - cache-on-failure: "true" - cache-all-crates: "true" + qa: true - name: Install dependencies run: | @@ -198,15 +192,9 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install rust toolchain - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 + - uses: ./.github/actions/install-rust with: - workspaces: "./py_rust -> target" - cache-on-failure: "true" - cache-all-crates: "true" - - name: Install nextest - uses: taiki-e/install-action@nextest + test: true - uses: actions/setup-python@v4 with: @@ -243,15 +231,9 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install rust toolchain - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 + - uses: ./.github/actions/install-rust with: - workspaces: "./rust -> target" - cache-on-failure: "true" - cache-all-crates: "true" - - name: Install nextest - uses: taiki-e/install-action@nextest + test: true - name: Run tests (linux) if: matrix.os == 'ubuntu-latest' diff --git a/.gitignore b/.gitignore index f8f250cb..0d204723 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,22 @@ # Ignore private folder and any file with private in the name: -private/ +**/**/private/ *private* # Except for zetch files: !*private*.zetch.* !*private*.*.zetch -process_data/ -logs/ +**/**/process_data/ +**/**/logs/ # Tempate files .cop.*.yml scratch_space.ipynb -node_modules/ +**/**/node_modules/ + +# These are backup files generated by rustfmt +**/*.rs.bk # Files flagged for ignore: which have .gitignore. inside them: *.gitignore.* @@ -52,6 +55,10 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST +# leptos build files: +**/**/static/rsite/ +# Compiled css: +**/**/static/css/ # PyInstaller # Usually these files are written by a python script from a template @@ -64,25 +71,27 @@ pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports -htmlcov/ +**/**/htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache -coverage/ +**/**/coverage/ nosetests.xml coverage.xml *.cover *.py,cover -.hypothesis/ -.pytest_cache/ -.ruff_cache/ -cover/ -.nox/ +**/**/.hypothesis/ +**/**/.pytest_cache/ +**/**/.ruff_cache/ +**/**/cover/ +**/**/.nox/ *.prof prof/ -perf_profiles/ +**/**/perf_profiles/ +**/**/test-results/ +**/**/playwright-report/ # Translations *.mo @@ -95,21 +104,21 @@ db.sqlite3 db.sqlite3-journal # Flask stuff: -instance/ +**/**/instance/ .webassets-cache # Scrapy stuff: .scrapy # PyBuilder -.pybuilder/ -target/ +**/**/.pybuilder/ +**/**/target/ # Jupyter Notebook .ipynb_checkpoints # IPython -profile_default/ +**/**/profile_default/ ipython_config.py # pyenv @@ -125,7 +134,7 @@ ipython_config.py #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ +**/**/__pypackages__/ # Celery stuff celerybeat-schedule @@ -135,22 +144,22 @@ celerybeat.pid *.sage.py # Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ +**/**/.env +**/**/.venv +**/**/env/ +**/**/venv/ +**/**/ENV/ +**/**/env.bak/ +**/**/venv.bak/ # mkdocs local build directory: -site/ -docs/js_ref/ -docs/rust_ref/ +**/**/site/ +**/**/docs/js_ref/ +**/**/docs/rust_ref/ .staticrypt.json # Vscode internals -.vscode/ +**/**/.vscode/ # Spyder project settings .spyderproject @@ -160,25 +169,25 @@ docs/rust_ref/ .ropeproject # mypy -.mypy_cache/ +**/**/.mypy_cache/ .dmypy.json dmypy.json # Pyre type checker -.pyre/ +**/**/.pyre/ # pytype static type analyzer -.pytype/ +**/**/.pytype/ # Cython debug symbols -cython_debug/ +**/**/cython_debug/ # Pycharm -.idea/ +**/**/.idea/ # Mac stuff .DS_Store -template_test_build/ +**/**/template_test_build/ # User diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7145c20b..731e54f9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/zakstucke/zetch - rev: v0.0.10 + rev: v0.0.16 hooks: - id: zetch @@ -48,6 +48,8 @@ repos: args: # Ignore don't error on specific words that always fail: (foo,bar,baz...) - -L=crate + - -L=numer + - -L=anull # Ruff: linting and formatting for python code: - repo: https://github.com/astral-sh/ruff-pre-commit @@ -76,23 +78,22 @@ repos: - id: cargo-fmt name: cargo-fmt description: "Format files with cargo fmt." - entry: cargo fmt - language: rust + language: system types: [rust] + entry: cargo fmt args: [--manifest-path=./rust/Cargo.toml, --] - - id: cargo-hack-check - name: cargo-check-each-feature - description: Check the package for errors. - entry: cargo hack check - language: rust + - id: cargo-rust-check + name: cargo-rust-check + description: Check the rust package for errors using cargo check on needed targets and features. + entry: ./dev_scripts/test.sh cargo_rust_check + language: system types: [rust] - args: [--manifest-path=./rust/Cargo.toml, --each-feature, --no-dev-deps] pass_filenames: false - id: cargo-clippy name: cargo-clippy description: Lint rust sources entry: cargo clippy - language: rust + language: system args: ["--manifest-path", "./rust/Cargo.toml", "--all-features", "--", "-D", "warnings"] types: [rust] pass_filenames: false @@ -104,22 +105,22 @@ repos: name: cargo-fmt description: "Format files with cargo fmt." entry: cargo fmt - language: rust + language: system types: [rust] args: [--manifest-path=./py_rust/Cargo.toml, --] - - id: cargo-hack-check - name: cargo-check-each-feature - description: Check the package for errors. - entry: cargo hack check - language: rust + - id: cargo-py-rust-check + name: cargo-py-rust-check + description: Check the rust-backed python package for errors using cargo check on needed targets + and features. + entry: ./dev_scripts/test.sh cargo_py_rust_check + language: system types: [rust] - args: [--manifest-path=./py_rust/Cargo.toml, --each-feature, --no-dev-deps] pass_filenames: false - id: cargo-clippy name: cargo-clippy description: Lint rust sources entry: cargo clippy - language: rust + language: system args: ["--manifest-path", "./py_rust/Cargo.toml", "--all-features", "--", "-D", "warnings"] types: [rust] pass_filenames: false diff --git a/.zetch.lock b/.zetch.lock index 08a6f165..d46d3c47 100644 --- a/.zetch.lock +++ b/.zetch.lock @@ -1,23 +1,24 @@ { - "version": "0.0.10", + "version": "0.0.16", "files": { - "docs/CODE_OF_CONDUCT.zetch.md": "bf106326ffc75f5167cfde27c997c77c6b97c843a9e392b564355d0e70e50b97", - "js/tsconfig.zetch.json": "fb5d57b825bb3c2f6dd4254bf939f2444e52946622a7f93b91e3acb75876ebbc", - "docs/index.zetch.md": "0dd2c30854a3e110c37b08c068c09e80f6e459017f123930a9be5ed08b34fece", "CODE_OF_CONDUCT.zetch.md": "bf106326ffc75f5167cfde27c997c77c6b97c843a9e392b564355d0e70e50b97", - "py/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", "CONTRIBUTING.zetch.md": "bace46dc064746b54cf472eba960d934d705c2f83120b865a4b47032ff1552c5", - "docs/CONTRIBUTING.zetch.md": "bace46dc064746b54cf472eba960d934d705c2f83120b865a4b47032ff1552c5", - "rust/README.zetch.md": "0dd2c30854a3e110c37b08c068c09e80f6e459017f123930a9be5ed08b34fece", + "LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", "README.zetch.md": "5e44429bf29ed38799d08bbf375435dd58516002c8dcf7f6f5cf47628cc29182", + "docs/CODE_OF_CONDUCT.zetch.md": "bf106326ffc75f5167cfde27c997c77c6b97c843a9e392b564355d0e70e50b97", + "docs/CONTRIBUTING.zetch.md": "bace46dc064746b54cf472eba960d934d705c2f83120b865a4b47032ff1552c5", + "docs/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", + "docs/index.zetch.md": "0dd2c30854a3e110c37b08c068c09e80f6e459017f123930a9be5ed08b34fece", + "js/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", "js/README.zetch.md": "0dd2c30854a3e110c37b08c068c09e80f6e459017f123930a9be5ed08b34fece", + "js/tsconfig.zetch.json": "20974867286079e9cff0945f28efa3d77b49a7d75ab4f7106969c44239df97c6", + "opencollector.yaml.zetch": "7bc57cf34798098de165251bee40b14905e46189e781f38597ed5debb1dfd4f7", + "py/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", "py/README.zetch.md": "0dd2c30854a3e110c37b08c068c09e80f6e459017f123930a9be5ed08b34fece", "py_rust/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", - "LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", - "docs/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", "py_rust/README.zetch.md": "0dd2c30854a3e110c37b08c068c09e80f6e459017f123930a9be5ed08b34fece", "rust/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", - "js/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b", - "opencollector.yaml.zetch": "678a691ae64d7f9893e8799ea657842fe051b3fcce4da568969d8de070a29393" + "rust/README.zetch.md": "0dd2c30854a3e110c37b08c068c09e80f6e459017f123930a9be5ed08b34fece", + "rust/pkg/LICENSE.zetch.md": "d2c12e539d357957b950a54a5477c3a9f87bd2b3ee707be7a4db7adaf5aacc2b" } } \ No newline at end of file diff --git a/bitbazaar.code-workspace b/bitbazaar.code-workspace index bbf8db99..9fb521a6 100644 --- a/bitbazaar.code-workspace +++ b/bitbazaar.code-workspace @@ -15,6 +15,14 @@ ], "rust-analyzer.rustfmt.extraArgs": [], "rust-analyzer.cargo.features": "all", // Enable all features in cargo.toml for type hinting + // Use a separate target dir to prevent it messing with other processes (used to cause locks etc): + // https://github.com/rust-lang/rust-analyzer/issues/6007 + "rust-analyzer.server.extraEnv": { + "CARGO_TARGET_DIR": "target/analyzer" + }, + "rust-analyzer.check.extraArgs": [ + "--target-dir=target/analyzer" + ], "biome.lspBin": "/usr/local/bin/biome", // Path to the biome binary installed in initial_setup.sh "editor.codeActionsOnSave": { @@ -99,29 +107,6 @@ "tag:yaml.org,2002:python/name:material.extensions.emoji.twemoji", "tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format" ], - // For the tailwind extension if using: - // Note: biome class sorting should be possible soon: https://github.com/biomejs/biome/issues/1274 - "tailwindCSS.experimental.classRegex": [ - // Allows tailwind autocomplete to work in "classnames" function and "cx" shorthand for it. - // https://github.com/tailwindlabs/tailwindcss/issues/7553 - ["(?:classnames|cx)\\(([^)]*)\\)", "\"([^\"]*)\""], - ["(?:classnames|cx)\\(([^)]*)\\)", "'([^']*')"], - ["(?:classnames|cx)\\(([^)]*)\\)", "`([^`]*`)"], - - // Also allow it to work in any variable starting with Classes: - "Classes=\"([^\"]*)", //
- "Classes = \"([^\"]*)", // *.Classes = "..." - "Classes={\"([^\"}]*)", //
- "Classes={`([^`}]*)", //
- "Classes: \"([^\"]*)", //
- "Classes: `([^`]*)" //
- ], - "tailwindCSS.files.exclude": [ - "**/.git/**", - "**/node_modules/**", - "**/venv/**", - "**/.venv/**" - ], "scss.lint.unknownAtRules": "ignore", "search.useIgnoreFiles": false, // Otherwise, things in .gitignore will not be searchable, its better to exclude them manually: "notebook.output.textLineLimit": 100, @@ -141,7 +126,8 @@ "**/.git/**": true, "**/ipynb_checkpoints/**": true, "**/.ipynb": true, - "**/target/**": true + "**/target/**": true, + "**/prof/**": true }, "files.watcherExclude": { "**/.venv/**": true, diff --git a/dev_scripts/_scr_setup/setup.sh b/dev_scripts/_scr_setup/setup.sh index ee7f2032..629484a3 100755 --- a/dev_scripts/_scr_setup/setup.sh +++ b/dev_scripts/_scr_setup/setup.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # If no function name provided, print a list of all the functions: if [ $# -eq 0 ]; then diff --git a/dev_scripts/docs.sh b/dev_scripts/docs.sh index 404f2d25..a373ac78 100755 --- a/dev_scripts/docs.sh +++ b/dev_scripts/docs.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # Exit on error # Builds the nested js site: diff --git a/dev_scripts/initial_setup.sh b/dev_scripts/initial_setup.sh index 4fc49932..5fb98b3a 100755 --- a/dev_scripts/initial_setup.sh +++ b/dev_scripts/initial_setup.sh @@ -1,8 +1,50 @@ -#!/bin/bash +#!/usr/bin/env bash # Stop on error: set -e +# Useful for platform matching, can use like: +# if is_arm; then +# echo "arm" +# else +# echo "not arm" +# fi +is_arm() { + if [ "$(uname -m)" == "arm64" ] || [ "$(uname -m)" == "aarch64" ]; then + return 0 # Return true + else + return 1 # Return false + fi +} + + +_ensure_zellij () { + target_version="0.40.1" + old_version=$(./dev_scripts/utils.sh match_substring 'zellij (.*)' "$(zellij --version 2>&1)" || echo "") + if [ "$old_version" != "$target_version" ]; then + echo "Installing zelliji version $target_version..." + + if [ "$(uname)" == "Darwin" ]; then + plat="apple-darwin" + elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + plat="unknown-linux-musl" + fi + + if is_arm; then + arch="aarch64" + else + arch="x86_64" + fi + + curl -L https://github.com/zellij-org/zellij/releases/download/v$target_version/zellij-$arch-$plat.tar.gz -o zellij.tar.gz -f + tar -xzf zellij.tar.gz + rm zellij.tar.gz + chmod +x zellij + sudo mv zellij /usr/local/bin + echo "zellij version $target_version installed!" + fi +} + # Pass in the version number _install_yaml_fmt () { echo "Installing yamlfmt version $1..." @@ -21,99 +63,120 @@ _install_yaml_fmt () { } -_install_openobserve() { - echo "Installing openobserve version $1..." - - # os lowercase: - OS=$(uname -s | tr '[:upper:]' '[:lower:]') - ARCH=$(uname -m) +_ensure_go () { + if ! command -v go > /dev/null 2>&1; then + echo "go toolchain not found, installing..." + go_version="1.22.3" + if is_arm; then + arch="arm64" + else + arch="amd64" + fi + if [ "$(uname)" == "Darwin" ]; then + plat="darwin" + elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + plat="linux" + fi - echo "Downloading openobserve version $1 for ${OS}-${ARCH}..." - curl -L https://github.com/openobserve/openobserve/releases/download/v$1/openobserve-v$1-${OS}-${ARCH}.tar.gz -o openobserve.tar.gz -f - tar -xzf openobserve.tar.gz - rm openobserve.tar.gz - chmod +x openobserve - sudo mv openobserve /usr/local/bin + curl -L https://go.dev/dl/go${go_version}.${plat}-${arch}.tar.gz -o go_src -f + sudo tar -C /usr/local -xzf go_src + rm go_src + echo "export GOPATH=~/go" >> ~/.profile && source ~/.profile + echo "Setting PATH to include golang binaries" + echo "export PATH='$PATH':/usr/local/go/bin:$GOPATH/bin" >> ~/.profile && source ~/.profile + fi } _ensure_openobserve() { - req_ver="$1" - - if [[ -z "$req_ver" ]]; then - echo "openobserve version not provided!" - exit 1 - fi + target_version="0.10.5" + old_version=$(./dev_scripts/utils.sh match_substring 'openobserve v(.*)' "$(openobserve --version 2>/dev/null)" || echo "") - if version=$(openobserve --version 2>/dev/null); then - # Will be "openobserve v$ver", make sure starts with "openobserve v" and remove that: - if [[ ! "$version" =~ ^openobserve\ v ]]; then - echo "openobserve version not found in expected format, expected 'openobserve vx.x.x', got '$version'!" - exit 1 - fi + if [ "$old_version" != "$target_version" ]; then + echo "Installing openobserve version $target_version..." - # Strip prefix: - version=${version#openobserve v} - - if [[ "$version" == "$req_ver" ]]; then - echo "openobserve already installed with correct version $version!" + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + if is_arm; then + ARCH="arm64" else - echo "openobserve incorrect version, upgrading to $version..." - _install_openobserve $req_ver + ARCH="amd64" fi - else - _install_openobserve $req_ver - fi -} - -_install_otlp_collector () { - echo "Installing otlp_collector version $1..." - # os lowercase: - OS=$(uname -s | tr '[:upper:]' '[:lower:]') - ARCH=$(uname -m) - - # If ARCH == aarch64, replace with arm64: - if [ "${ARCH}" == "aarch64" ]; then - ARCH="arm64" + curl -L https://github.com/openobserve/openobserve/releases/download/v$target_version/openobserve-v$target_version-${OS}-${ARCH}.tar.gz -o openobserve.tar.gz -f + tar -xzf openobserve.tar.gz + rm openobserve.tar.gz + chmod +x openobserve + sudo mv openobserve /usr/local/bin fi - - echo "Downloading otlp_collector version $1 for ${OS}-${ARCH}..." - curl -L https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v$1/otelcol-contrib_$1_${OS}_${ARCH}.tar.gz \ - -o otlp_collector.tar.gz -f - tar -xzf otlp_collector.tar.gz - rm otlp_collector.tar.gz - # Comes out as otelcol-contrib: - mv otelcol-contrib otlp_collector - chmod +x otlp_collector - sudo mv otlp_collector /usr/local/bin } -_ensure_otlp_collector() { - req_ver="$1" - - if [[ -z "$req_ver" ]]; then - echo "otlp_collector version not provided!" - exit 1 - fi - - if version=$(otlp_collector --version 2>/dev/null); then - # Will be "otelcol-contrib version $ver", make sure starts with "otelcol-contrib version " and remove that: - if [[ ! "$version" =~ ^otelcol-contrib\ version\ ]]; then - echo "otlp_collector version not found in expected format, expected 'otelcol-contrib version x.x.x', got '$version'!" - exit 1 - fi - - # Strip prefix: - version=${version#otelcol-contrib version } - - if [[ "$version" == "$req_ver" ]]; then - echo "otlp_collector already installed with correct version $version!" +# We don't use the default released binary as it's 250MB! +# Instead, we compile a custom one that's only 22MB. +# We manage this by removing a bunch of features we don't need. +# For custom compilation docs, see https://opentelemetry.io/docs/collector/custom-collector/ +# For a full list of components to add, see https://github.com/open-telemetry/opentelemetry-collector/blob/main/cmd/otelcorecol/builder-config.yaml +_ensure_otlp_collector () { + target_version="0.100.0" + install_path="$HOME/compiled_otlp_collector" + build_path="$install_path/build" + active_version_path="$install_path/active_version.txt" + + # If active_version_path file doesn't exist, or doesn't contain target version, need to install/reinstall: + if [ ! -f $active_version_path ] || [ "$(cat $active_version_path)" != "$target_version" ]; then + echo "otlp_collector version $target_version needs installing..." + + # We're compiling the otlp go project from src, hence need go: + _ensure_go + + # Remove old artifacts: + rm -rf $install_path + mkdir -p $install_path + cd $install_path + + if is_arm; then + arch="arm64" else - echo "otlp_collector incorrect version, upgrading to $version..." - _install_otlp_collector $req_ver + arch="amd64" fi - else - _install_otlp_collector $req_ver + if [ "$(uname)" == "Darwin" ]; then + plat="darwin" + elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + plat="linux" + fi + + # Install the builder: + curl --proto '=https' --tlsv1.2 -fL -o ocb \ + https://github.com/open-telemetry/opentelemetry-collector/releases/download/cmd%2Fbuilder%2Fv${target_version}/ocb_${target_version}_${plat}_${arch} + chmod +x ocb + + # Write the builder config yaml file the builder needs, this specifies which components we're actually going to build: + printf "%s\n" "dist:" \ + " name: otelcol-dev" \ + " description: Basic OTel Collector distribution for Developers" \ + " output_path: ./otelcol-dev" \ + " otelcol_version: ${target_version}" \ + "" \ + "exporters:" \ + " - gomod: go.opentelemetry.io/collector/exporter/otlphttpexporter v${target_version}" \ + "" \ + "processors:" \ + " - gomod: go.opentelemetry.io/collector/processor/batchprocessor v${target_version}" \ + " - gomod: go.opentelemetry.io/collector/processor/memorylimiterprocessor v${target_version}" \ + "" \ + "receivers:" \ + " - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v${target_version}" \ + "" > builder-config.yaml + + # Run the builder: + go env + ./ocb --config builder-config.yaml --verbose + + # Make the binary executable: + chmod +x otelcol-dev/otelcol-dev + # Move the outputted binary to /usr/local/bin and rename to "otlp_collector" + sudo mv otelcol-dev/otelcol-dev /usr/local/bin/otlp_collector + + # Update the active version so won't re-install next time unless version changes: + echo $target_version > $active_version_path fi } @@ -159,6 +222,35 @@ _ensure_biome() { fi } +_install_cargo_hack () { + # Get host target + host=$(rustc -Vv | grep host | sed 's/host: //') + # Download binary and install to $HOME/.cargo/bin + curl --proto '=https' --tlsv1.2 -fsSL https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-$host.tar.gz | tar xzf - -C "$HOME/.cargo/bin" +} + +_ensure_cargo_hack () { + if version=$(./dev_scripts/utils.sh match_substring 'cargo-hack (.*)' "$(cargo hack --version)"); then + echo "cargo-hack already installed with version $version" + else + echo "cargo-hack not installed, installing..." + _install_cargo_hack + fi +} + +_ensure_gnuplot () { + if command -v gnuplot > /dev/null 2>&1; then + echo "gnuplot already installed" + else + echo "gnuplot could not be found, installing..." + if [ "$(uname)" == "Darwin" ]; then + brew install gnuplot + elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + sudo apt-get install -y gnuplot + fi + fi +} + initial_setup () { # Install useful local directories (might be unused): mkdir -p ./process_data @@ -172,9 +264,14 @@ initial_setup () { pipx install zetch fi + # Make sure zellij installed and correct version: + _ensure_zellij + # Make sure openobserve is installed for dev open telemetry logging: - _ensure_openobserve "0.8.0" - _ensure_otlp_collector "0.94.0" + _ensure_openobserve + + # Make sure otlp collector is installed as the interface between our processes and openobserve: + _ensure_otlp_collector # Make sure biome is installed for linting and formatting various files: _ensure_biome "1.5.3" @@ -200,13 +297,21 @@ initial_setup () { _install_yaml_fmt $yamlfmt_req_ver fi + # Make sure nextest is installed: cargo install cargo-nextest --locked + # Make sure cargo-hack is installed: + _ensure_cargo_hack + # Make sure gnuplot installed for criterion benchmarks: + _ensure_gnuplot # Install pre-commit if not already: pipx install pre-commit || true pre-commit install + # Make sure pdm global cache being used to speed up installs: + pdm config install.cache on + echo "Setting up docs..." cd docs # Effectively simulating pdm init but won't modify upstream pyproject.toml or use existing active venv: @@ -223,13 +328,6 @@ initial_setup () { pdm install -G:all cd .. - echo "Setting up js..." - cd js - npm i - cd .. - - - echo "Setting up rust backed python project..." ./dev_scripts/py_rust.sh ensure_venv diff --git a/dev_scripts/pkg.sh b/dev_scripts/pkg.sh index e9404ff4..eeaddc2c 100755 --- a/dev_scripts/pkg.sh +++ b/dev_scripts/pkg.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Stop on error: set -e diff --git a/dev_scripts/process.sh b/dev_scripts/process.sh index 60e5aac3..75169031 100755 --- a/dev_scripts/process.sh +++ b/dev_scripts/process.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Stop on error: set -e @@ -104,25 +104,23 @@ list() { # Terminate a process and all of its child processes terminate() { local parent_pid="$1" - local IS_CHILD=$2 # Terminate the child processes of the parent PID local child_pids=$(pgrep -P "$parent_pid") for pid in $child_pids; do - terminate "$pid" "true" + terminate "$pid" done # Terminate the parent PID if ps -p "$parent_pid" > /dev/null; then - if [ "$IS_CHILD" = "true" ]; then - echo "Terminating child: $parent_pid" - else - echo "Terminating root: $parent_pid" - fi - # Or true to not error if the process is already dead: - kill -9 "$parent_pid" > /dev/null 2>&1 || true + # The || true to ignore when the pid is already dead: + # Send SIGTERM and SIGHUP, then if it's still alive after 15 seconds, send SIGKILL + kill -15 "$parent_pid" 2>/dev/null || true + kill -1 "$parent_pid" 2>/dev/null || true + { sleep 15; kill -9 "$parent_pid" 2>/dev/null || true; } & fi } + # Has to come at the end of these files: source ./dev_scripts/_scr_setup/setup.sh "$@" \ No newline at end of file diff --git a/dev_scripts/py_rust.sh b/dev_scripts/py_rust.sh index bd4d8af8..77b2a57e 100755 --- a/dev_scripts/py_rust.sh +++ b/dev_scripts/py_rust.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Stop on error: set -e @@ -21,10 +21,8 @@ ensure_venv () { source ./py_rust/.venv/bin/activate fi - ./dev_scripts/utils.sh py_install_if_missing typing-extensions - ./dev_scripts/utils.sh py_install_if_missing maturin - ./dev_scripts/utils.sh py_install_if_missing pyright - ./dev_scripts/utils.sh py_install_if_missing pytest + # Install any dev requirements that aren't managed by maturin: + pip install -r ./py_rust/dev_requirements.txt } diff --git a/dev_scripts/run.sh b/dev_scripts/run.sh index 2b8743bd..4aee27da 100755 --- a/dev_scripts/run.sh +++ b/dev_scripts/run.sh @@ -1,49 +1,26 @@ -#!/bin/bash +#!/usr/bin/env bash # Stop on error: set -e -# Prep for running top-level services -_prep () { - # A custom env version may have been used before, reset zetch to make sure not the case. - zetch - - # Start open telemetry collector and openobserve in the background: - ./dev_scripts/run.sh collector - ./dev_scripts/run.sh oo +# Debug+prod prep for running top-level services +_shared_run_prep () { } -# Starts the open telemetry collector in the background to collect open telemetry data -collector () { - if [ "$(./dev_scripts/utils.sh in_ci)" = "true" ]; then - echo "In CI, not starting open telemetry collector." - else - prefix="otlp_col_" - - # Stop any current open observer processes: - ./dev_scripts/process.sh stop $prefix - - # Run the process: - ./dev_scripts/process.sh start "${prefix}bitbazaar" "otlp_collector --config $(pwd)/opencollector.yaml" - fi -} - -# Starts the openobserve server in the background to look at dev logs/traces/metrics +# Starts the openobserve server: oo () { - if [ "$(./dev_scripts/utils.sh in_ci)" = "true" ]; then - echo "In CI, not starting openobserver." - else - prefix="oo_" - - # Stop any current open observer processes: - ./dev_scripts/process.sh stop $prefix + ZO_ROOT_USER_EMAIL="dev@dev.com" ZO_ROOT_USER_PASSWORD="pass" \ + ZO_DATA_DIR="$(pwd)/process_data/openobserve" \ + openobserve +} - ZO_ROOT_USER_EMAIL="dev@dev.com" ZO_ROOT_USER_PASSWORD="pass" \ - ZO_DATA_DIR="$(pwd)/process_data/openobserve" \ - ./dev_scripts/process.sh start "${prefix}bitbazaar" "openobserve" - fi +# Open telemetry collector: +collector () { + # Make sure stopped before starting again (zj SIGHUP doesn't seem to be respected by otlp_collector) + pkill -9 otlp_collector &> /dev/null || true + otlp_collector --config $(pwd)/opencollector.yaml } # Has to come at the end of these files: diff --git a/dev_scripts/test.sh b/dev_scripts/test.sh index 0861e2c0..612f5e70 100755 --- a/dev_scripts/test.sh +++ b/dev_scripts/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Stop on error: set -e @@ -8,13 +8,13 @@ all () { ./dev_scripts/test.sh qa echo "Python..." - ./dev_scripts/test.sh py + ./dev_scripts/test.sh py -n auto echo "Javascript..." ./dev_scripts/test.sh js echo "Python Rust..." - ./dev_scripts/test.sh py_rust + ./dev_scripts/test.sh py_rust -n auto echo "Rust..." ./dev_scripts/test.sh rust @@ -60,17 +60,13 @@ qa () { } py () { - ./dev_scripts/run.sh collector # Needed for open telemetry tests in bitbazaar - cd ./py/ # Check for COVERAGE=False/false, which is set in some workflow runs to make faster: if [[ "$COVERAGE" == "False" ]] || [[ "$COVERAGE" == "false" ]]; then echo "COVERAGE=False/false, not running coverage" pdm run pytest $@ else - pdm run coverage run --parallel -m pytest $@ - pdm run coverage combine - pdm run coverage report + pdm run pytest --cov=./bitbazaar/ $@ fi cd .. } @@ -103,17 +99,35 @@ py_rust () { fi # Have to specify to compile in debug mode (meaning it will use the install_debug call above) - cargo nextest run --cargo-profile dev --all-features + cargo nextest run --all-features python -m pytest $@ deactivate cd .. } +# Used internally by pre-commit: +cargo_py_rust_check () { + # This will go through and check with no features, each feature on it's own, and all features respectively. + # Note: won't do unnecessary checks, e.g. if no features will only run cargo check once. + cargo hack check --manifest-path=./py_rust/Cargo.toml --each-feature +} + rust () { - ./dev_scripts/run.sh collector # Needed for open telemetry tests in bitbazaar - cargo nextest run --cargo-profile dev --manifest-path ./rust/Cargo.toml --all-features $@ + cargo nextest run --manifest-path ./rust/Cargo.toml --all-features $@ +} + +rust_bench () { + + cargo bench --manifest-path ./rust/Cargo.toml --all-features $@ +} + +# Used internally by pre-commit: +cargo_rust_check () { + # This will go through and check with no features, each feature on it's own, and all features respectively using cargo hack. + # Note: won't do unnecessary checks, e.g. if no features in this project will only run cargo check once. + cargo hack check --manifest-path=./rust/Cargo.toml --each-feature } docs () { diff --git a/dev_scripts/utils.sh b/dev_scripts/utils.sh index 858cf762..37601610 100755 --- a/dev_scripts/utils.sh +++ b/dev_scripts/utils.sh @@ -1,12 +1,79 @@ -#!/bin/bash +#!/usr/bin/env bash # Stop on error: set -e -# Run commands in parallel. E.g. run_parallel "sleep 1" "sleep 1" "sleep 1" -run_parallel () { - # --halt now,fail=1 stops all processes if any of the error - parallel --ungroup -j 0 --halt now,fail=1 ::: "$@" +pre_till_commit () { + ./dev_scripts/test.sh pre_till_success + git commit "$@" +} + +# Run commands in parallel. E.g. run_endless_parallel "sleep 1" "sleep 1" "sleep 1" +# - if any exit, exit all. Because this is for endless parallelism, if something goes down, the whole thing should. +# Originally used gnu-parallel line below, but caused problems in prod and with child processes: +# parallel --ungroup -j 0 --halt now,done=1 ::: "$@" +run_endless_parallel () { + # 4.3 needed for wait -n: + ensure_bash_version + + # Store each pid, so can kill all and their children if one fails: + local pids=() + local succeeded_pids=() + + # Called after error and on ctrl-c to kill any remaining processes: + kill_unfinished() { + # Terminate any that didn't succeed. + # That script will send sigterm/hup first, + # then 15 seconds later kill if still active. + for pid in "${pids[@]}"; do + if [[ ! ${succeeded_pids[@]} =~ $pide ]]; then + ./dev_scripts/process.sh terminate "$pid" + fi + done + } + + # Make sure to still kill background processes if e.g. ctrl-c is pressed: + on_external_kill() { + kill_unfinished + exit 1 + } + trap 'on_external_kill' INT + + # Fire off each command in the background: + for cmd in "$@"; do + eval "$cmd" & pid=$! + pids+=($pid) + done + + for cmd in "$@"; do + # Disable exit on error temporarily, would break the inside block: + set +e + # Wait for ANY PID to finish + # The || true is needed because we call "set -e" on all our scripts. + wait -n + exit_status=$? + finished_pid=$! + # Re-enable exit on error: + set -e + + if [ $exit_status -eq 0 ]; then + succeeded_pids+=($finished_pid) + fi + + # In both cases of successful exit and not, + # kill all remaining PID's and return with the code of the original. + # Find the command so we can print its exit code: + finished_cmd="" + for i in "${!pids[@]}"; do + if [ "${pids[i]}" -eq "$finished_pid" ]; then + finished_cmd="${@:i:i+1}" + break + fi + done + echo "Cmd exited with code=$exit_status: \"$finished_cmd\". Forcefully exiting remaining commands..." + kill_unfinished + return $exit_status + done } py_install_if_missing () { @@ -63,5 +130,34 @@ match_substring () { anypython ./dev_scripts/_internal/match_substring.py "$1" "$2" } +# Return a random id to use +rand_id () { + echo $(openssl rand -hex 3) +} + +run_in_new_terminal () { + if [ "$(uname)" == "Darwin" ]; then + osascript -e "tell application \"Terminal\" to do script \"cd $(pwd); $1\"" + else + x-terminal-emulator -e "$1" + fi +} + +docker_stop_all_containers () { + sudo docker stop $(sudo docker ps -a -q) 2>/dev/null || true +} + +docker_delete_all_containers () { + sudo docker rm -vf $(sudo docker ps -a -q) 2>/dev/null || true +} + +docker_prune_volumes () { + sudo docker volume prune +} + +docker_delete_all_images () { + sudo docker rmi -f $(sudo docker images -a -q) 2>/dev/null || true +} + # Has to come at the end of these files: source ./dev_scripts/_scr_setup/setup.sh "$@" diff --git a/dev_scripts/zj.sh b/dev_scripts/zj.sh new file mode 100755 index 00000000..b87d3e98 --- /dev/null +++ b/dev_scripts/zj.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Stop on error: +set -e + +wait_for_session () { + # Wait for session to be available, checking every 0.01 seconds, after 2 seconds break with error: + found=false + for i in {1..200}; do + if zellij list-sessions | grep -q $1; then + found=true + # If i isn't 1, meaning literally just been created, + # wait an extra 0.05 seconds to make sure everything is ready: + if [ "$i" != 1 ]; then + sleep 0.05 + fi + break + fi + sleep 0.01 + done + if [ "$found" = false ]; then + echo "Session $1 not found after 2 seconds" + exit 1 + fi +} + +# Has to come at the end of these files: +source ./dev_scripts/_scr_setup/setup.sh "$@" diff --git a/js/bun.lockb b/js/bun.lockb index be720c30c61ead270d1de15f29c3dbeda762749a..53710a20c1e03d817c4167b23c10ddb146bc01d0 100755 GIT binary patch delta 10437 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.0.15", "tsup": ">=7.2.0", + "bun-types": ">=1.0.15", "typescript": ">=5.2.2", + "@happy-dom/global-registrator": "13.3.8", "@testing-library/react": "^14.1.2", + "wouter": "<3.2.0", "@types/chroma-js": "^2.4.2", "@types/html-minifier-terser": "7.0.1", "@types/react": "^18.2.31", @@ -30,7 +35,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "vite": "^5.0.11", - "wouter": "^3.0.0-rc.1" + "wouter": "^3.2.0" }, "dependencies": { "install": "^0.13.0", diff --git a/js/tsconfig.json b/js/tsconfig.json index 4dc0775a..ec7a37a1 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -32,8 +32,7 @@ "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "downlevelIteration": true, - "allowSyntheticDefaultImports": true - }, + "allowSyntheticDefaultImports": true }, "exclude": [ "**/*.spec.(cjs|mjs|js|jsx|ts|tsx)", "**/*.test.(cjs|mjs|js|jsx|ts|tsx)", diff --git a/js/tsconfig.zetch.json b/js/tsconfig.zetch.json index efb5a08a..ffcbaa53 100644 --- a/js/tsconfig.zetch.json +++ b/js/tsconfig.zetch.json @@ -32,8 +32,7 @@ "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "downlevelIteration": true, - "allowSyntheticDefaultImports": true - }, + "allowSyntheticDefaultImports": true }, "exclude": [ "**/*.spec.(cjs|mjs|js|jsx|ts|tsx)", "**/*.test.(cjs|mjs|js|jsx|ts|tsx)", diff --git a/opencollector.yaml b/opencollector.yaml index 2024bff1..3126f215 100644 --- a/opencollector.yaml +++ b/opencollector.yaml @@ -30,15 +30,6 @@ exporters: headers: Authorization: Basic ZGV2QGRldi5jb206cGFzcw== stream-name: default - # Writes all opentelemetry logs, traces, metrics to a file, useful for testing: - file/debug_file_writing: - path: /home/runner/work/bitbazaar/bitbazaar/logs/otlp_telemetry_out.log - rotation: - max_megabytes: 10 - max_days: 3 - max_backups: 3 - localtime: true - flush_interval: 1 # Write every 1 seconds service: telemetry: @@ -51,16 +42,13 @@ service: processors: [memory_limiter, batch] exporters: - otlphttp - - file/debug_file_writing traces: receivers: [otlp] processors: [memory_limiter, batch] exporters: - otlphttp - - file/debug_file_writing metrics: # Don't bother with memory limiting for metrics, traces and logs will be the largest consumers of memory receivers: [otlp] processors: [batch] exporters: - otlphttp - - file/debug_file_writing diff --git a/opencollector.yaml.zetch b/opencollector.yaml.zetch index 9357b37a..38d7cadb 100644 --- a/opencollector.yaml.zetch +++ b/opencollector.yaml.zetch @@ -31,18 +31,6 @@ exporters: Authorization: {{ OTLP_OO_AUTH }} stream-name: default -{%- if DEBUG %} - # Writes all opentelemetry logs, traces, metrics to a file, useful for testing: - file/debug_file_writing: - path: {{ ROOT_DIR }}/logs/otlp_telemetry_out.log - rotation: - max_megabytes: 10 - max_days: 3 - max_backups: 3 - localtime: true - flush_interval: 1 # Write every 1 seconds -{%- endif %} - service: telemetry: logs: @@ -54,22 +42,13 @@ service: processors: [memory_limiter, batch] exporters: - otlphttp - {%- if DEBUG %} - - file/debug_file_writing - {%- endif %} traces: receivers: [otlp] processors: [memory_limiter, batch] exporters: - otlphttp - {%- if DEBUG %} - - file/debug_file_writing - {%- endif %} metrics: # Don't bother with memory limiting for metrics, traces and logs will be the largest consumers of memory receivers: [otlp] processors: [batch] exporters: - - otlphttp - {%- if DEBUG %} - - file/debug_file_writing - {%- endif %} \ No newline at end of file + - otlphttp \ No newline at end of file diff --git a/py/bitbazaar/__init__.py b/py/bitbazaar/__init__.py index 01fa1c71..18aacfd6 100644 --- a/py/bitbazaar/__init__.py +++ b/py/bitbazaar/__init__.py @@ -3,4 +3,5 @@ from importlib.metadata import version __version__ = version("bitbazaar") + __all__ = ["__version__"] diff --git a/py/bitbazaar/log/_formatting.py b/py/bitbazaar/log/_formatting.py index c2949712..5b6d63b9 100644 --- a/py/bitbazaar/log/_formatting.py +++ b/py/bitbazaar/log/_formatting.py @@ -258,7 +258,7 @@ def _fmt_where_parts(log: LogRecord, is_file: bool, show_sids: bool) -> str: else: return f"[dim italic] where {parts_str}[/]\n" else: - return "" + return "" # pragma: no cover def _format_duration(nanoseconds: int) -> str: # pragma: no cover diff --git a/py/pdm.lock b/py/pdm.lock index 11d19416..027b39e6 100644 --- a/py/pdm.lock +++ b/py/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev", "test"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:746c185679af35306f95dd2390c7c455677e9a0d8308a5d526f64b3a935d3bdc" +content_hash = "sha256:6dc882a8f2b7d773efed1cb36f92c2762bfc0b03d2a98791a1a62db70f5ce1e8" [[package]] name = "appnope" @@ -32,27 +32,15 @@ files = [ {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, ] -[[package]] -name = "backoff" -version = "2.2.1" -requires_python = ">=3.7,<4.0" -summary = "Function decoration for backoff and retry" -groups = ["default"] -marker = "python_version >= \"3.7\"" -files = [ - {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, - {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, -] - [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." groups = ["default"] files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -267,6 +255,17 @@ files = [ {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, ] +[[package]] +name = "execnet" +version = "2.1.1" +requires_python = ">=3.8" +summary = "execnet: rapid multi-Python deployment" +groups = ["test"] +files = [ + {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, + {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, +] + [[package]] name = "executing" version = "2.0.1" @@ -280,60 +279,60 @@ files = [ [[package]] name = "googleapis-common-protos" -version = "1.62.0" +version = "1.63.1" requires_python = ">=3.7" summary = "Common protobufs used in Google APIs" groups = ["default"] dependencies = [ - "protobuf!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0.dev0,>=3.19.5", + "protobuf!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0.dev0,>=3.19.5", ] files = [ - {file = "googleapis-common-protos-1.62.0.tar.gz", hash = "sha256:83f0ece9f94e5672cced82f592d2a5edf527a96ed1794f0bab36d5735c996277"}, - {file = "googleapis_common_protos-1.62.0-py2.py3-none-any.whl", hash = "sha256:4750113612205514f9f6aa4cb00d523a94f3e8c06c5ad2fee466387dc4875f07"}, + {file = "googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a"}, + {file = "googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877"}, ] [[package]] name = "grpcio" -version = "1.60.1" -requires_python = ">=3.7" +version = "1.64.1" +requires_python = ">=3.8" summary = "HTTP/2-based RPC framework" groups = ["default"] files = [ - {file = "grpcio-1.60.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:79ae0dc785504cb1e1788758c588c711f4e4a0195d70dff53db203c95a0bd303"}, - {file = "grpcio-1.60.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:4eec8b8c1c2c9b7125508ff7c89d5701bf933c99d3910e446ed531cd16ad5d87"}, - {file = "grpcio-1.60.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8c9554ca8e26241dabe7951aa1fa03a1ba0856688ecd7e7bdbdd286ebc272e4c"}, - {file = "grpcio-1.60.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91422ba785a8e7a18725b1dc40fbd88f08a5bb4c7f1b3e8739cab24b04fa8a03"}, - {file = "grpcio-1.60.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cba6209c96828711cb7c8fcb45ecef8c8859238baf15119daa1bef0f6c84bfe7"}, - {file = "grpcio-1.60.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c71be3f86d67d8d1311c6076a4ba3b75ba5703c0b856b4e691c9097f9b1e8bd2"}, - {file = "grpcio-1.60.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5ef6cfaf0d023c00002ba25d0751e5995fa0e4c9eec6cd263c30352662cbce"}, - {file = "grpcio-1.60.1-cp311-cp311-win32.whl", hash = "sha256:a09506eb48fa5493c58f946c46754ef22f3ec0df64f2b5149373ff31fb67f3dd"}, - {file = "grpcio-1.60.1-cp311-cp311-win_amd64.whl", hash = "sha256:49c9b6a510e3ed8df5f6f4f3c34d7fbf2d2cae048ee90a45cd7415abab72912c"}, - {file = "grpcio-1.60.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b58b855d0071575ea9c7bc0d84a06d2edfbfccec52e9657864386381a7ce1ae9"}, - {file = "grpcio-1.60.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:a731ac5cffc34dac62053e0da90f0c0b8560396a19f69d9703e88240c8f05858"}, - {file = "grpcio-1.60.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:cf77f8cf2a651fbd869fbdcb4a1931464189cd210abc4cfad357f1cacc8642a6"}, - {file = "grpcio-1.60.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c557e94e91a983e5b1e9c60076a8fd79fea1e7e06848eb2e48d0ccfb30f6e073"}, - {file = "grpcio-1.60.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:069fe2aeee02dfd2135d562d0663fe70fbb69d5eed6eb3389042a7e963b54de8"}, - {file = "grpcio-1.60.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb0af13433dbbd1c806e671d81ec75bd324af6ef75171fd7815ca3074fe32bfe"}, - {file = "grpcio-1.60.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2f44c32aef186bbba254129cea1df08a20be414144ac3bdf0e84b24e3f3b2e05"}, - {file = "grpcio-1.60.1-cp312-cp312-win32.whl", hash = "sha256:a212e5dea1a4182e40cd3e4067ee46be9d10418092ce3627475e995cca95de21"}, - {file = "grpcio-1.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:6e490fa5f7f5326222cb9f0b78f207a2b218a14edf39602e083d5f617354306f"}, - {file = "grpcio-1.60.1.tar.gz", hash = "sha256:dd1d3a8d1d2e50ad9b59e10aa7f07c7d1be2b367f3f2d33c5fade96ed5460962"}, + {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, + {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, + {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, + {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, + {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, + {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, + {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, + {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, + {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, ] [[package]] name = "idna" -version = "3.6" +version = "3.7" requires_python = ">=3.5" summary = "Internationalized Domain Names in Applications (IDNA)" groups = ["default"] files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] name = "importlib-metadata" -version = "6.11.0" +version = "7.1.0" requires_python = ">=3.8" summary = "Read metadata from Python packages" groups = ["default"] @@ -341,8 +340,8 @@ dependencies = [ "zipp>=0.5", ] files = [ - {file = "importlib_metadata-6.11.0-py3-none-any.whl", hash = "sha256:f0afba6205ad8f8947c7d338b5342d5db2afbfd82f9cbef7879a9539cc12eb9b"}, - {file = "importlib_metadata-6.11.0.tar.gz", hash = "sha256:1231cf92d825c9e03cfc4da076a16de6422c863558229ea0b22b675657463443"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [[package]] @@ -543,130 +542,130 @@ files = [ [[package]] name = "opentelemetry-api" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.25.0" +requires_python = ">=3.8" summary = "OpenTelemetry Python API" groups = ["default"] dependencies = [ "deprecated>=1.2.6", - "importlib-metadata<7.0,>=6.0", + "importlib-metadata<=7.1,>=6.0", ] files = [ - {file = "opentelemetry_api-1.22.0-py3-none-any.whl", hash = "sha256:43621514301a7e9f5d06dd8013a1b450f30c2e9372b8e30aaeb4562abf2ce034"}, - {file = "opentelemetry_api-1.22.0.tar.gz", hash = "sha256:15ae4ca925ecf9cfdfb7a709250846fbb08072260fca08ade78056c502b86bed"}, + {file = "opentelemetry_api-1.25.0-py3-none-any.whl", hash = "sha256:757fa1aa020a0f8fa139f8959e53dec2051cc26b832e76fa839a6d76ecefd737"}, + {file = "opentelemetry_api-1.25.0.tar.gz", hash = "sha256:77c4985f62f2614e42ce77ee4c9da5fa5f0bc1e1821085e9a47533a9323ae869"}, ] [[package]] name = "opentelemetry-exporter-otlp" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.25.0" +requires_python = ">=3.8" summary = "OpenTelemetry Collector Exporters" groups = ["default"] dependencies = [ - "opentelemetry-exporter-otlp-proto-grpc==1.22.0", - "opentelemetry-exporter-otlp-proto-http==1.22.0", + "opentelemetry-exporter-otlp-proto-grpc==1.25.0", + "opentelemetry-exporter-otlp-proto-http==1.25.0", ] files = [ - {file = "opentelemetry_exporter_otlp-1.22.0-py3-none-any.whl", hash = "sha256:cb03a1cbf300e12b47690858be13dd26fe2f60b2610204959f3497cd6645e3a1"}, - {file = "opentelemetry_exporter_otlp-1.22.0.tar.gz", hash = "sha256:309a7d4dc67602801f15818e110ce452e78989886aaab5d37e7cf7f55f1d3d27"}, + {file = "opentelemetry_exporter_otlp-1.25.0-py3-none-any.whl", hash = "sha256:d67a831757014a3bc3174e4cd629ae1493b7ba8d189e8a007003cacb9f1a6b60"}, + {file = "opentelemetry_exporter_otlp-1.25.0.tar.gz", hash = "sha256:ce03199c1680a845f82e12c0a6a8f61036048c07ec7a0bd943142aca8fa6ced0"}, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.25.0" +requires_python = ">=3.8" summary = "OpenTelemetry Protobuf encoding" groups = ["default"] dependencies = [ - "backoff<3.0.0,>=1.10.0; python_version >= \"3.7\"", - "opentelemetry-proto==1.22.0", + "opentelemetry-proto==1.25.0", ] files = [ - {file = "opentelemetry_exporter_otlp_proto_common-1.22.0-py3-none-any.whl", hash = "sha256:3f2538bec5312587f8676c332b3747f54c89fe6364803a807e217af4603201fa"}, - {file = "opentelemetry_exporter_otlp_proto_common-1.22.0.tar.gz", hash = "sha256:71ae2f81bc6d6fe408d06388826edc8933759b2ca3a97d24054507dc7cfce52d"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.25.0-py3-none-any.whl", hash = "sha256:15637b7d580c2675f70246563363775b4e6de947871e01d0f4e3881d1848d693"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.25.0.tar.gz", hash = "sha256:c93f4e30da4eee02bacd1e004eb82ce4da143a2f8e15b987a9f603e0a85407d3"}, ] [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.25.0" +requires_python = ">=3.8" summary = "OpenTelemetry Collector Protobuf over gRPC Exporter" groups = ["default"] dependencies = [ - "backoff<3.0.0,>=1.10.0; python_version >= \"3.7\"", "deprecated>=1.2.6", "googleapis-common-protos~=1.52", "grpcio<2.0.0,>=1.0.0", "opentelemetry-api~=1.15", - "opentelemetry-exporter-otlp-proto-common==1.22.0", - "opentelemetry-proto==1.22.0", - "opentelemetry-sdk~=1.22.0", + "opentelemetry-exporter-otlp-proto-common==1.25.0", + "opentelemetry-proto==1.25.0", + "opentelemetry-sdk~=1.25.0", ] files = [ - {file = "opentelemetry_exporter_otlp_proto_grpc-1.22.0-py3-none-any.whl", hash = "sha256:b5bcadc129272004316a455e9081216d3380c1fc2231a928ea6a70aa90e173fb"}, - {file = "opentelemetry_exporter_otlp_proto_grpc-1.22.0.tar.gz", hash = "sha256:1e0e5aa4bbabc74942f06f268deffd94851d12a8dc30b02527472ef1729fe5b1"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.25.0-py3-none-any.whl", hash = "sha256:3131028f0c0a155a64c430ca600fd658e8e37043cb13209f0109db5c1a3e4eb4"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.25.0.tar.gz", hash = "sha256:c0b1661415acec5af87625587efa1ccab68b873745ca0ee96b69bb1042087eac"}, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.25.0" +requires_python = ">=3.8" summary = "OpenTelemetry Collector Protobuf over HTTP Exporter" groups = ["default"] dependencies = [ - "backoff<3.0.0,>=1.10.0; python_version >= \"3.7\"", "deprecated>=1.2.6", "googleapis-common-protos~=1.52", "opentelemetry-api~=1.15", - "opentelemetry-exporter-otlp-proto-common==1.22.0", - "opentelemetry-proto==1.22.0", - "opentelemetry-sdk~=1.22.0", + "opentelemetry-exporter-otlp-proto-common==1.25.0", + "opentelemetry-proto==1.25.0", + "opentelemetry-sdk~=1.25.0", "requests~=2.7", ] files = [ - {file = "opentelemetry_exporter_otlp_proto_http-1.22.0-py3-none-any.whl", hash = "sha256:e002e842190af45b91dc55a97789d0b98e4308c88d886b16049ee90e17a4d396"}, - {file = "opentelemetry_exporter_otlp_proto_http-1.22.0.tar.gz", hash = "sha256:79ed108981ec68d5f7985355bca32003c2f3a5be1534a96d62d5861b758a82f4"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.25.0-py3-none-any.whl", hash = "sha256:2eca686ee11b27acd28198b3ea5e5863a53d1266b91cda47c839d95d5e0541a6"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.25.0.tar.gz", hash = "sha256:9f8723859e37c75183ea7afa73a3542f01d0fd274a5b97487ea24cb683d7d684"}, ] [[package]] name = "opentelemetry-proto" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.25.0" +requires_python = ">=3.8" summary = "OpenTelemetry Python Proto" groups = ["default"] dependencies = [ "protobuf<5.0,>=3.19", ] files = [ - {file = "opentelemetry_proto-1.22.0-py3-none-any.whl", hash = "sha256:ce7188d22c75b6d0fe53e7fb58501613d0feade5139538e79dedd9420610fa0c"}, - {file = "opentelemetry_proto-1.22.0.tar.gz", hash = "sha256:9ec29169286029f17ca34ec1f3455802ffb90131642d2f545ece9a63e8f69003"}, + {file = "opentelemetry_proto-1.25.0-py3-none-any.whl", hash = "sha256:f07e3341c78d835d9b86665903b199893befa5e98866f63d22b00d0b7ca4972f"}, + {file = "opentelemetry_proto-1.25.0.tar.gz", hash = "sha256:35b6ef9dc4a9f7853ecc5006738ad40443701e52c26099e197895cbda8b815a3"}, ] [[package]] name = "opentelemetry-sdk" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.25.0" +requires_python = ">=3.8" summary = "OpenTelemetry Python SDK" groups = ["default"] dependencies = [ - "opentelemetry-api==1.22.0", - "opentelemetry-semantic-conventions==0.43b0", + "opentelemetry-api==1.25.0", + "opentelemetry-semantic-conventions==0.46b0", "typing-extensions>=3.7.4", ] files = [ - {file = "opentelemetry_sdk-1.22.0-py3-none-any.whl", hash = "sha256:a730555713d7c8931657612a88a141e3a4fe6eb5523d9e2d5a8b1e673d76efa6"}, - {file = "opentelemetry_sdk-1.22.0.tar.gz", hash = "sha256:45267ac1f38a431fc2eb5d6e0c0d83afc0b78de57ac345488aa58c28c17991d0"}, + {file = "opentelemetry_sdk-1.25.0-py3-none-any.whl", hash = "sha256:d97ff7ec4b351692e9d5a15af570c693b8715ad78b8aafbec5c7100fe966b4c9"}, + {file = "opentelemetry_sdk-1.25.0.tar.gz", hash = "sha256:ce7fc319c57707ef5bf8b74fb9f8ebdb8bfafbe11898410e0d2a761d08a98ec7"}, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.43b0" -requires_python = ">=3.7" +version = "0.46b0" +requires_python = ">=3.8" summary = "OpenTelemetry Semantic Conventions" groups = ["default"] +dependencies = [ + "opentelemetry-api==1.25.0", +] files = [ - {file = "opentelemetry_semantic_conventions-0.43b0-py3-none-any.whl", hash = "sha256:291284d7c1bf15fdaddf309b3bd6d3b7ce12a253cec6d27144439819a15d8445"}, - {file = "opentelemetry_semantic_conventions-0.43b0.tar.gz", hash = "sha256:b9576fb890df479626fa624e88dde42d3d60b8b6c8ae1152ad157a8b97358635"}, + {file = "opentelemetry_semantic_conventions-0.46b0-py3-none-any.whl", hash = "sha256:6daef4ef9fa51d51855d9f8e0ccd3a1bd59e0e545abe99ac6203804e36ab3e07"}, + {file = "opentelemetry_semantic_conventions-0.46b0.tar.gz", hash = "sha256:fbc982ecbb6a6e90869b15c1673be90bd18c8a56ff1cffc0864e38e2edffaefa"}, ] [[package]] @@ -743,18 +742,18 @@ files = [ [[package]] name = "protobuf" -version = "4.25.2" +version = "4.25.3" requires_python = ">=3.8" summary = "" groups = ["default"] files = [ - {file = "protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6"}, - {file = "protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9"}, - {file = "protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d"}, - {file = "protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62"}, - {file = "protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020"}, - {file = "protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830"}, - {file = "protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e"}, + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, ] [[package]] @@ -848,6 +847,36 @@ files = [ {file = "pytest-8.0.0.tar.gz", hash = "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c"}, ] +[[package]] +name = "pytest-cov" +version = "5.0.0" +requires_python = ">=3.8" +summary = "Pytest plugin for measuring coverage." +groups = ["test"] +dependencies = [ + "coverage[toml]>=5.2.1", + "pytest>=4.6", +] +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[[package]] +name = "pytest-xdist" +version = "3.5.0" +requires_python = ">=3.7" +summary = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +groups = ["test"] +dependencies = [ + "execnet>=1.1", + "pytest>=6.2.0", +] +files = [ + {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"}, + {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"}, +] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -935,8 +964,8 @@ files = [ [[package]] name = "requests" -version = "2.31.0" -requires_python = ">=3.7" +version = "2.32.3" +requires_python = ">=3.8" summary = "Python HTTP for Humans." groups = ["default"] dependencies = [ @@ -946,13 +975,13 @@ dependencies = [ "urllib3<3,>=1.21.1", ] files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" requires_python = ">=3.7.0" summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" groups = ["default"] @@ -961,8 +990,8 @@ dependencies = [ "pygments<3.0.0,>=2.13.0", ] files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [[package]] @@ -1035,24 +1064,24 @@ files = [ [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" groups = ["default"] files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "urllib3" -version = "2.2.0" +version = "2.2.1" requires_python = ">=3.8" summary = "HTTP library with thread-safe connection pooling, file post, and more." groups = ["default"] files = [ - {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, - {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [[package]] @@ -1098,11 +1127,11 @@ files = [ [[package]] name = "zipp" -version = "3.17.0" +version = "3.19.2" requires_python = ">=3.8" summary = "Backport of pathlib-compatible object wrapper for zip files" groups = ["default"] files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] diff --git a/py/pyproject.toml b/py/pyproject.toml index 68ec4bd4..0c9f83f2 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -13,7 +13,7 @@ include = ['/README.md', '/LICENSE.md', '/bitbazaar', '/tests', '/requirements.t [tool.pdm] distribution = true [tool.pdm.dev-dependencies] -test = ["pyright>=1.1.329", "pytest>=7.4.2", "coverage[toml]>=7.3.2"] +test = ["pyright>=1.1.329", "pytest>=7.4.2", "pytest-cov>=4.1.0", "pytest-xdist==3.5.0"] dev = ["ipykernel>=6.25.2"] [project] @@ -68,3 +68,6 @@ source = ["bitbazaar"] [tool.coverage.report] show_missing = true fail_under = 100 +# Cli entrypoint if being used: +[project.scripts] +bitbazaar = 'bitbazaar.cli:cli' diff --git a/py/tests/test_version.py b/py/tests/test_version.py new file mode 100644 index 00000000..06bb2268 --- /dev/null +++ b/py/tests/test_version.py @@ -0,0 +1,8 @@ +import bitbazaar + + +def test_version(): + """Just a default example version test.""" + from importlib.metadata import version + + assert bitbazaar.__version__ == version("bitbazaar") diff --git a/py_rust/.config/nextest.toml b/py_rust/.config/nextest.toml new file mode 100644 index 00000000..57d48425 --- /dev/null +++ b/py_rust/.config/nextest.toml @@ -0,0 +1,112 @@ +# <--- DEFAULTS BELOW FROM https://nexte.st/book/configuration.html ---> + +[store] +# The directory under the workspace root at which nextest-related files are +# written. Profile-specific storage is currently written to dir/. +dir = "target/nextest" + +# This section defines the default nextest profile. Custom profiles are layered +# on top of the default profile. +[profile.default] +# "retries" defines the number of times a test should be retried. If set to a +# non-zero value, tests that succeed on a subsequent attempt will be marked as +# flaky. Can be overridden through the `--retries` option. +# Examples +# * retries = 3 +# * retries = { backoff = "fixed", count = 2, delay = "1s" } +# * retries = { backoff = "exponential", count = 10, delay = "1s", jitter = true, max-delay = "10s" } +retries = 0 + +# The number of threads to run tests with. Supported values are either an integer or +# the string "num-cpus". Can be overridden through the `--test-threads` option. +test-threads = "num-cpus" + +# The number of threads required for each test. This is generally used in overrides to +# mark certain tests as heavier than others. However, it can also be set as a global parameter. +threads-required = 1 + +# Show these test statuses in the output. +# +# The possible values this can take are: +# * none: no output +# * fail: show failed (including exec-failed) tests +# * retry: show flaky and retried tests +# * slow: show slow tests +# * pass: show passed tests +# * skip: show skipped tests (most useful for CI) +# * all: all of the above +# +# Each value includes all the values above it; for example, "slow" includes +# failed and retried tests. +# +# Can be overridden through the `--status-level` flag. +status-level = "pass" + +# Similar to status-level, show these test statuses at the end of the run. +final-status-level = "flaky" + +# "failure-output" defines when standard output and standard error for failing tests are produced. +# Accepted values are +# * "immediate": output failures as soon as they happen +# * "final": output failures at the end of the test run +# * "immediate-final": output failures as soon as they happen and at the end of +# the test run; combination of "immediate" and "final" +# * "never": don't output failures at all +# +# For large test suites and CI it is generally useful to use "immediate-final". +# +# Can be overridden through the `--failure-output` option. +failure-output = "immediate" + +# "success-output" controls production of standard output and standard error on success. This should +# generally be set to "never". +success-output = "never" + +# Cancel the test run on the first failure. For CI runs, consider setting this +# to false. +fail-fast = true + +# Treat a test that takes longer than the configured 'period' as slow, and print a message. +# See for more information. +# +# Optional: specify the parameter 'terminate-after' with a non-zero integer, +# which will cause slow tests to be terminated after the specified number of +# periods have passed. +# Example: slow-timeout = { period = "60s", terminate-after = 2 } +slow-timeout = { period = "60s" } + +# Treat a test as leaky if after the process is shut down, standard output and standard error +# aren't closed within this duration. +# +# This usually happens in case of a test that creates a child process and lets it inherit those +# handles, but doesn't clean the child process up (especially when it fails). +# +# See for more information. +leak-timeout = "100ms" + +[profile.default.junit] +# Output a JUnit report into the given file inside 'store.dir/'. +# If unspecified, JUnit is not written out. + +# path = "junit.xml" + +# The name of the top-level "report" element in JUnit report. If aggregating +# reports across different test runs, it may be useful to provide separate names +# for each report. +report-name = "nextest-run" + +# Whether standard output and standard error for passing tests should be stored in the JUnit report. +# Output is stored in the and elements of the element. +store-success-output = false + +# Whether standard output and standard error for failing tests should be stored in the JUnit report. +# Output is stored in the and elements of the element. +# +# Note that if a description can be extracted from the output, it is always stored in the +# element. +store-failure-output = true + +# This profile is activated if MIRI_SYSROOT is set. +[profile.default-miri] +# Miri tests take up a lot of memory, so only run 1 test at a time by default. +test-threads = 1 diff --git a/py_rust/Cargo.lock b/py_rust/Cargo.lock index b4dde1a4..968f9f64 100644 --- a/py_rust/Cargo.lock +++ b/py_rust/Cargo.lock @@ -17,15 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -41,60 +32,18 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" - -[[package]] -name = "anstyle-parse" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - [[package]] name = "anyhow" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + [[package]] name = "async-trait" version = "0.1.77" @@ -178,14 +127,23 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitbazaar" -version = "0.0.31" +version = "0.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa2497814b06c0b3550f63d9e65c914ebf355725473ae873dcc5ac5ba886db9" +checksum = "ddffcb103ced7be1e95fc199e955ffb9ed752c997de2d51c27bea5fbd1a79328" dependencies = [ "chrono", - "clap", + "chrono-humanize", "colored", "comfy-table", "error-stack", @@ -198,11 +156,8 @@ dependencies = [ "opentelemetry-semantic-conventions", "opentelemetry_sdk", "parking_lot", - "regex", "rustc_version", "serde", - "serde_json", - "strum", "time", "tracing", "tracing-appender", @@ -210,6 +165,7 @@ dependencies = [ "tracing-log", "tracing-opentelemetry", "tracing-subscriber", + "tracing-subscriber-wasm", ] [[package]] @@ -217,6 +173,7 @@ name = "bitbazaar_rs" version = "0.0.3" dependencies = [ "bitbazaar", + "colored", "error-stack", "parking_lot", "pyo3", @@ -278,51 +235,14 @@ dependencies = [ ] [[package]] -name = "clap" -version = "4.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.0" +name = "chrono-humanize" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.49", + "chrono", ] -[[package]] -name = "clap_lex" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "colored" version = "2.1.0" @@ -330,7 +250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -431,6 +351,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -518,6 +447,172 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net", + "gloo-render", + "gloo-storage", + "gloo-timers", + "gloo-utils", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events", + "gloo-utils", + "serde", + "serde-wasm-bindgen", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 0.2.11", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "h2" version = "0.3.24" @@ -799,7 +894,7 @@ checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1197,35 +1292,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "regex" -version = "1.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1274,6 +1340,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.196" @@ -1296,6 +1373,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1327,15 +1416,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys", ] -[[package]] -name = "strsim" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" - [[package]] name = "strum" version = "0.25.0" @@ -1478,7 +1561,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1675,6 +1758,17 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tracing-subscriber-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79804e80980173c6c8e53d98508eb24a2dbc4ee17a3e8d2ca8e5bad6bf13a898" +dependencies = [ + "gloo", + "tracing", + "tracing-subscriber", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -1705,12 +1799,6 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "valuable" version = "0.1.0" @@ -1757,6 +1845,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.91" @@ -1786,6 +1886,16 @@ version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +[[package]] +name = "web-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "web-time" version = "0.2.4" @@ -1836,15 +1946,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - [[package]] name = "windows-targets" version = "0.48.5" diff --git a/py_rust/Cargo.toml b/py_rust/Cargo.toml index 36d2e9a4..705fc380 100644 --- a/py_rust/Cargo.toml +++ b/py_rust/Cargo.toml @@ -14,19 +14,19 @@ path = "src/lib.rs" # Add your dependencies here [dependencies] +colored = '2' tracing = "0.1" error-stack = "0.4" -bitbazaar = { version = '0.0.31', features = ["opentelemetry"] } +bitbazaar = { version = '0.0.52', features = ["timing", "opentelemetry-grpc"] } pyo3 = { version = '0.20.0', features = ['extension-module', 'chrono', 'generate-import-lib'] } parking_lot = { version = "0.12", features = ['deadlock_detection', 'serde'] } strum = { version = '0.25', features = ['derive'] } +[profile.release] +strip = "debuginfo" # Note: true or "symbols" seems to break static c linking e.g. with ffmpeg. + [profile.profiler] inherits = "release" # Adds on top of the default release profile incremental = true debug = true strip = false - -# Modifying the test runtime (from release is just so much faster) -[profile.test] -inherits = "release" # Adds on top of the default release profile diff --git a/py_rust/dev_requirements.txt b/py_rust/dev_requirements.txt new file mode 100644 index 00000000..065f5078 --- /dev/null +++ b/py_rust/dev_requirements.txt @@ -0,0 +1,6 @@ +maturin==1.4.0 +typing-extensions==4.9.0 +pyright==1.1.351 +pytest==8.0.1 +pytest-xdist==3.5.0 +pytest-profiling==1.7.0 \ No newline at end of file diff --git a/py_rust/rustfmt.toml b/py_rust/rustfmt.toml index 8b137891..3a26366d 100644 --- a/py_rust/rustfmt.toml +++ b/py_rust/rustfmt.toml @@ -1 +1 @@ - +edition = "2021" diff --git a/py_rust/src/lib.rs b/py_rust/src/lib.rs index f69d209d..f38d6d74 100644 --- a/py_rust/src/lib.rs +++ b/py_rust/src/lib.rs @@ -1,12 +1,15 @@ +#![allow(clippy::module_inception)] +#![allow(clippy::type_complexity)] #![warn(clippy::disallowed_types)] +use colored::Colorize; use pyo3::prelude::*; mod utils; #[pyfunction] pub fn hello() -> String { - "Hello, World!".to_string() + "Hello, World!".cyan().to_string() } /// A Python module implemented in Rust. The name of this function must match diff --git a/py_rust/src/prelude.rs b/py_rust/src/prelude.rs index c668559e..458648f0 100644 --- a/py_rust/src/prelude.rs +++ b/py_rust/src/prelude.rs @@ -1,6 +1,10 @@ #[allow(unused_imports)] -pub use bitbazaar::{anyerr, errors::AnyErr, panic_on_err}; +pub use bitbazaar::{anyerr, err, errors::AnyErr, panic_on_err, panic_on_err_async}; #[allow(unused_imports)] -pub use error_stack::{Result, ResultExt}; +pub use error_stack::{Report, Result, ResultExt}; #[allow(unused_imports)] pub use tracing::{debug, error, info, warn}; + +/// Shorthand for a [`Result`] with a [`Report`] as the error variant +#[allow(dead_code)] +pub type RResult = Result>; diff --git a/py_rust/tests/test_basic.py b/py_rust/tests/test_basic.py deleted file mode 100644 index 876cf347..00000000 --- a/py_rust/tests/test_basic.py +++ /dev/null @@ -1,9 +0,0 @@ -import bitbazaar_rs - - -def test_hello(): - assert bitbazaar_rs.hello() == "Hello, World!" - - -def test_add(): - assert bitbazaar_rs.utils.add(1, 2) == 3 diff --git a/py_rust/tests/test_version.py b/py_rust/tests/test_version.py new file mode 100644 index 00000000..951bff27 --- /dev/null +++ b/py_rust/tests/test_version.py @@ -0,0 +1,8 @@ +import bitbazaar_rs + + +def test_version(): + """Just a default example version test.""" + from importlib.metadata import version + + assert bitbazaar_rs.__version__ == version("bitbazaar_rs") diff --git a/rust/.config/nextest.toml b/rust/.config/nextest.toml index a236338b..57d48425 100644 --- a/rust/.config/nextest.toml +++ b/rust/.config/nextest.toml @@ -1,12 +1,4 @@ -[test-groups] -serial = { max-threads = 1 } - -# A lot of the log tests use static variables or read from files for tests, causing conflicts if run in parallel: -[[profile.default.overrides]] -filter = 'test(log::)' -test-group = 'serial' - -# <--- DEFAULTS ---> +# <--- DEFAULTS BELOW FROM https://nexte.st/book/configuration.html ---> [store] # The directory under the workspace root at which nextest-related files are diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 511922bd..959debab 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -64,6 +64,17 @@ dependencies = [ "syn 2.0.49", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -154,6 +165,7 @@ dependencies = [ "colored", "comfy-table", "conch-parser", + "criterion", "deadpool-redis", "error-stack", "futures", @@ -178,7 +190,7 @@ dependencies = [ "serde", "serde_json", "sha1_smol", - "strum", + "strum 0.25.0", "sysinfo", "tempfile", "time", @@ -201,9 +213,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bumpalo" @@ -217,6 +229,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.83" @@ -234,9 +252,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -256,6 +274,17 @@ dependencies = [ "chrono", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags 1.3.2", + "textwrap", + "unicode-width", +] + [[package]] name = "colored" version = "2.1.0" @@ -268,9 +297,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "futures-core", @@ -282,13 +311,13 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" +checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ "crossterm", - "strum", - "strum_macros", + "strum 0.26.2", + "strum_macros 0.26.4", "unicode-width", ] @@ -317,6 +346,43 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "criterion" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "futures", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam-channel" version = "0.5.11" @@ -357,7 +423,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossterm_winapi", "libc", "parking_lot", @@ -373,11 +439,32 @@ dependencies = [ "winapi", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "deadpool" -version = "0.11.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144f5e4b9ce67c972acc225e71aefe6b21241276f94005024562874611064d30" +checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed" dependencies = [ "deadpool-runtime", "num_cpus", @@ -386,9 +473,9 @@ dependencies = [ [[package]] name = "deadpool-redis" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2244d421c9514eab2e1ce1aa1e3c9d5c7cbb9cf3d9bbcac21a6b27e6a868d84" +checksum = "7ff315fab2a7a42132352909afc81140d06b8bbfd1414b098ce278e3f95dd1b9" dependencies = [ "deadpool", "redis", @@ -396,9 +483,9 @@ dependencies = [ [[package]] name = "deadpool-runtime" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" dependencies = [ "tokio", ] @@ -412,6 +499,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + [[package]] name = "either" version = "1.10.0" @@ -420,9 +518,9 @@ checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -766,9 +864,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -783,6 +881,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + [[package]] name = "hashbrown" version = "0.12.3" @@ -801,11 +905,26 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "homedir" @@ -867,9 +986,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "httpdate" @@ -879,9 +998,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -936,14 +1055,134 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -1014,6 +1253,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.11" @@ -1099,11 +1344,11 @@ dependencies = [ [[package]] name = "normpath" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5" +checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1146,7 +1391,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -1174,6 +1419,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opentelemetry" version = "0.21.0" @@ -1371,6 +1622,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" + +[[package]] +name = "plotters-svg" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +dependencies = [ + "plotters-backend", +] + [[package]] name = "portpicker" version = "0.1.1" @@ -1485,9 +1764,9 @@ dependencies = [ [[package]] name = "redis" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6472825949c09872e8f2c50bde59fcefc17748b6be5c90fd67cd8b4daca73bfd" +checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec" dependencies = [ "async-trait", "bytes", @@ -1575,9 +1854,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.11.24" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64", "bytes", @@ -1659,7 +1938,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -1678,6 +1957,15 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1710,6 +1998,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.196" @@ -1784,22 +2082,47 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strum" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ - "strum_macros", + "strum_macros 0.25.3", ] +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" + [[package]] name = "strum_macros" version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.49", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", "proc-macro2", "quote", "rustversion", @@ -1834,6 +2157,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + [[package]] name = "sysinfo" version = "0.30.12" @@ -1882,6 +2216,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.57" @@ -1956,19 +2299,24 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "tinytemplate" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] [[package]] name = "tokio" @@ -2198,27 +2546,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-width" version = "0.1.11" @@ -2227,9 +2560,9 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -2242,11 +2575,23 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", ] @@ -2263,6 +2608,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2366,9 +2721,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -2386,6 +2741,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2579,9 +2943,9 @@ dependencies = [ [[package]] name = "wmi" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff298e96fd8ef6bb55dcb2a7fd2f26969f962bf428ffa6b267457dd804d64d8" +checksum = "fc2f0a4062ca522aad4705a2948fd4061b3857537990202a8ddd5af21607f79a" dependencies = [ "chrono", "futures", @@ -2590,3 +2954,82 @@ dependencies = [ "thiserror", "windows", ] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 20b204d7..900a777c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -11,6 +11,7 @@ name = "bitbazaar" crate-type = ["lib", "cdylib"] # cdylib allows things like wasm to compile path = "bitbazaar/lib.rs" + [build-dependencies] rustc_version = "0.4.0" @@ -18,49 +19,6 @@ rustc_version = "0.4.0" # For the features to show in docs.rs, need to tell it to include them: all-features = true -[features] -log-filter = ["dep:regex"] -chrono = ['dep:chrono', 'dep:chrono-humanize'] -timing = ['dep:comfy-table', 'chrono'] -cli = ['dep:normpath', 'dep:conch-parser', 'dep:homedir', 'chrono', 'dep:strum'] -system = ['dep:sysinfo'] -redis = [ - 'dep:tokio', - 'dep:deadpool-redis', - 'dep:redis', - "dep:redis-macros", - 'dep:sha1_smol', - 'dep:serde_json', - 'dep:rand', - 'dep:futures', - 'chrono', - 'dep:uuid', -] -opentelemetry-grpc = [ - 'dep:tracing-log', - 'dep:opentelemetry-appender-tracing', - 'dep:opentelemetry_sdk', - 'dep:tracing-opentelemetry', - 'dep:opentelemetry', - 'dep:opentelemetry-otlp', - 'dep:opentelemetry-semantic-conventions', - 'dep:http', - 'opentelemetry-otlp/grpc-tonic', -] -opentelemetry-http = [ # In general there's no point with this currently, made for wasm but otlp can't be used on wasm yet (tonic) - 'dep:tracing-log', - 'dep:opentelemetry-appender-tracing', - 'dep:opentelemetry_sdk', - 'dep:tracing-opentelemetry', - 'dep:opentelemetry', - 'dep:opentelemetry-otlp', - 'dep:opentelemetry-semantic-conventions', - 'dep:http', - 'opentelemetry-otlp/grpc-tonic', # Stupid needed but it currently is due to otlp internals - 'opentelemetry-otlp/http-proto', - 'opentelemetry-otlp/reqwest-client', -] - # Add your dependencies here [dependencies] tracing = "0.1" @@ -130,6 +88,7 @@ hostname = "0.3.1" [dev-dependencies] rstest = "0.18" +criterion = { version = "0.3", features = ["html_reports", "async_futures"] } portpicker = '0.1.1' tempfile = '3.8' tokio = { version = '1', features = ["rt-multi-thread", "macros"] } @@ -138,12 +97,60 @@ regex = "1" serde_json = "1" futures = "0.3" +# When adding new benches, they should be added like this with the name of the file in benches/: (obviously uncommented) +# [[bench]] +# name = "bench_tester" +# harness = false + +[features] +log-filter = ["dep:regex"] +chrono = ['dep:chrono', 'dep:chrono-humanize'] +timing = ['dep:comfy-table', 'chrono'] +cli = ['dep:normpath', 'dep:conch-parser', 'dep:homedir', 'chrono', 'dep:strum'] +system = ['dep:sysinfo'] +redis = [ + 'dep:tokio', + 'dep:deadpool-redis', + 'dep:redis', + "dep:redis-macros", + 'dep:sha1_smol', + 'dep:serde_json', + 'dep:rand', + 'dep:futures', + 'chrono', + 'dep:uuid', +] +opentelemetry-grpc = [ + 'dep:tracing-log', + 'dep:opentelemetry-appender-tracing', + 'dep:opentelemetry_sdk', + 'dep:tracing-opentelemetry', + 'dep:opentelemetry', + 'dep:opentelemetry-otlp', + 'dep:opentelemetry-semantic-conventions', + 'dep:http', + 'opentelemetry-otlp/grpc-tonic', +] +opentelemetry-http = [ # In general there's no point with this currently, made for wasm but otlp can't be used on wasm yet (tonic) + 'dep:tracing-log', + 'dep:opentelemetry-appender-tracing', + 'dep:opentelemetry_sdk', + 'dep:tracing-opentelemetry', + 'dep:opentelemetry', + 'dep:opentelemetry-otlp', + 'dep:opentelemetry-semantic-conventions', + 'dep:http', + 'opentelemetry-otlp/grpc-tonic', # Stupid needed but it currently is due to otlp internals + 'opentelemetry-otlp/http-proto', + 'opentelemetry-otlp/reqwest-client', +] + +[profile.release] +strip = "debuginfo" # Note: true or "symbols" seems to break static c linking e.g. with ffmpeg. + [profile.profiler] + inherits = "release" # Adds on top of the default release profile incremental = true debug = true strip = false - -# Modifying the test runtime (from release is just so much faster) -[profile.test] -inherits = "release" # Adds on top of the default release profile diff --git a/rust/benches/bench_setup_test.rs b/rust/benches/bench_setup_test.rs new file mode 100644 index 00000000..44289562 --- /dev/null +++ b/rust/benches/bench_setup_test.rs @@ -0,0 +1,25 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +fn fibonacci(n: u64) -> u64 { + let mut a = 0; + let mut b = 1; + + match n { + 0 => b, + _ => { + for _ in 0..n { + let c = a + b; + a = b; + b = c; + } + b + } + } +} + +pub fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20)))); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/rust/bitbazaar/lib.rs b/rust/bitbazaar/lib.rs index f47fc3b4..9059bead 100644 --- a/rust/bitbazaar/lib.rs +++ b/rust/bitbazaar/lib.rs @@ -1,3 +1,5 @@ +#![allow(clippy::module_inception)] +#![allow(clippy::type_complexity)] #![warn(clippy::disallowed_types)] #![warn(missing_docs)] diff --git a/rust/bitbazaar/prelude.rs b/rust/bitbazaar/prelude.rs index cdd9d362..621e16f7 100644 --- a/rust/bitbazaar/prelude.rs +++ b/rust/bitbazaar/prelude.rs @@ -1,7 +1,12 @@ #[allow(unused_imports)] -pub use error_stack::{Result, ResultExt}; +pub use error_stack::{Report, Result, ResultExt}; #[allow(unused_imports)] pub use tracing::{debug, error, info, warn}; #[allow(unused_imports)] pub use crate::{anyerr, err, errors::prelude::*, panic_on_err, panic_on_err_async}; + +// TODO maybe upstream in bb +/// Shorthand for a [`Result`] with a [`Report`] as the error variant +#[allow(dead_code)] +pub type RResult = Result>; diff --git a/rust/rustfmt.toml b/rust/rustfmt.toml index 455c8209..3a26366d 100644 --- a/rust/rustfmt.toml +++ b/rust/rustfmt.toml @@ -1,2 +1 @@ -imports_granularity = "Crate" -group_imports = "StdExternalCrate" +edition = "2021" diff --git a/zetch.config.toml b/zetch.config.toml index 2cb379c3..7c913355 100644 --- a/zetch.config.toml +++ b/zetch.config.toml @@ -1,4 +1,4 @@ -#:schema https://raw.githubusercontent.com/zakstucke/zetch/v0.0.10/py_rust/src/config/schema.json +#:schema https://raw.githubusercontent.com/zakstucke/zetch/v0.0.16/py_rust/src/config/schema.json ignore_files = [".gitignore"] @@ -12,14 +12,17 @@ custom_extensions = [] commands = ["./dev_scripts/pkg.sh update_versions"] [context.static] -PY_VERSION = { value = "0.0.7" } -JS_VERSION = { value = "0.0.29" } -RUST_VERSION = { value = "0.0.52" } -PY_RUST_VERSION = { value = "0.0.3" } +PY_VERSION = "0.0.7" +JS_VERSION = "0.0.29" +RUST_VERSION = "0.0.52" +PY_RUST_VERSION = "0.0.3" JS_ALIASES = { value = '{ "@root": "./bitbazaar", "@scripts": "./scripts" }', coerce = "json" } +# This will be updated manually in locations that need it: (using ./dev_scripts/utils.sh rand_id) +BUILD_UNIQUE_ID = "foobar" [context.env] DEBUG = { default = true, coerce = "bool" } +IN_DOCKER = { default = false, coerce = "bool" } OTLP_OO_ENDPOINT = { default = "http://localhost:5080/api/default/" } OTLP_OO_AUTH = { default = "Basic ZGV2QGRldi5jb206cGFzcw==" } # Not security issue! This is just base64('dev@dev.com:pass')