content: #:schema https://json.schemastore.org/pyproject.json
  
  [project]
  authors = [{ name = "ONEKEY", email = "support@onekey.com" }]
  
  # Operators supported: https://packaging.python.org/en/latest/specifications/version-specifiers/#id5
  dependencies = [
    "arpy>=2.3.0",
    "attrs>=23.1.0",
    "click>=8.1.7",
    "cryptography>=41.0",
    "dissect-cstruct>=4.0,<5.0",
    "jefferson>=0.4.5",
    "lark>=1.1.8",
    "lief>=0.16.1",
    "lz4>=4.3.2,!=4.4.3",        # 4.4.3 doesn't have aarch64 wheels https://github.com/python-lz4/python-lz4/pull/298
    "plotext>=4.2.0,<6.0",
    "pluggy>=1.3.0",
    "pydantic>=2.0",
    "pyfatfs>=1.0.5",
    "pymdown-extensions>=10.15",
    "pyperscan>=0.3.0",
    "python-magic>=0.4.27",
    "pyzstd>0.16.2",
    "rarfile>=4.1",
    "rich>=13.3.5",
    "structlog>=24.1.0",
    "treelib>=1.7.0",
    "ubi-reader>=0.8.9",
  ]
  description = "Extract files from any kind of container formats"
  license = { text = "MIT" }
  name = "unblob"
  readme = "README.md"
  requires-python = ">=3.9"
  version = "25.5.26"
  
  [project.scripts]
  unblob = "unblob.cli:main"
  
  [dependency-groups]
  dev = [
    "atheris>=2.3,<3.0; sys_platform == 'linux' and python_version < '3.12'",
    "pre-commit>=3.5,<5.0",
    "pyright>=1.1.349",
    "pytest-cov>=3,<7",
    "pytest>=8.0.0",
    "pyyaml>=6.0.1",
    "ruff==0.12.12",
    "taplo>=0.9.3",
  ]
  docs = [
    "cairosvg>=2.7.1",
    "mkdocs-material==9.6.21",
    "mkdocstrings-python>=1.8.0",
    "mkdocstrings>=0.30,<0.31",
    "pillow>=10.2.0,<12.0",
    "pymdown-extensions>=10.15",
  ]
  
  [tool.ruff]
  target-version = "py39"
  
  [tool.ruff.lint]
  select = [
    "A",      # flake8-builtins
    "ARG",    # flake8-unused-arguments
    "B",      # flake8-bugbear
    "C4",     # flake8-comprehensions
    "C90",    # mccabe
    "COM818", # flake8-commas; trailing-comma-on-bare-tuple
    "D",      # pydocstyle
    "E",      # pycodestyle (errors)
    "F",      # pyflakes
    "FBT",    # flake8-boolean-trap
    "G",      # flake8-logging-format
    "I",      # isort
    "N",      # pep8-naming
    "PIE",    # flake8-pie
    "PL",     # pylint
    "PT",     # flake8-pytest-style
    "PTH",    # flake8-use-pathlib
    "RET",    # flake8-return
    "RSE",    # flake8-raise
    "RUF",    # ruff's own lints
    "S",      # flake8-bandit
    "SIM",    # flake8-simplify
    "SLF",    # flake8-self
    "T10",    # flake8-debugger
    "T20",    # flake8-print
    "TC",     # flake8-type-checking
    "TID251", # flake8-tidy-imports; banned-api
    "TRY",    # tryceratops
    "UP",     # pyupgrade
    "W",      # pycodestyle (warnings)
  ]
  
  ignore = [
    "A005",    # stdlib-module-shadowing:                 E.g. `unblog.logging` collides with stdlib `logging`. It's okay for us
    "B027",    # empty-method-without-abstract-decorator: It is okay to have empty methods in abstract classes
    "D1",      # undocumented-*:                          We are not documenting every public symbol
    "D203",    # one-blank-line-before-class:             D211 (no-blank-line-before-class) is used instead
    "D213",    # multi-line-summary-second-line:          D212 (multi-line-summary-first-line) is used instead
    "E501",    # line-too-long:                           Let black handle line length violations
    "N818",    # error-suffix-on-exception-name:          Exception names can be meaningful without smurfs
    "PLC0414", # useless-import-alias:                    Enable explicitly re-exporting public symbols
    "PLR09",   # too-many-{arguments,branches,...}:       We do not want to impose hard limits
    "PTH201",  # path-constructor-current-directory:      Having "." explicitly passed expresses intent more clearly
    "RUF012",  # mutable-class-default:                   Wants to annotate things like `Handler.PATTERNS` with `ClassVar`, producing noise
    "S101",    # assert:                                  Enable usage of asserts
    "S603",    # subprocess-without-shell-equals-true:    This check just highlights, that we should check for untrusted inputs
    "S607",    # start-process-with-partial-path:         In our case, the convenience of allowing partial paths outweighs the risks
    "TRY003",  # raise-vanilla-args:                      We are adding contextual information in exception messages
    "TRY400",  # error-instead-of-exception:              It is okay to log without backtrace in except blocks
  ]
  
  # Do not remove unused imports automatically in __init__.py files
  ignore-init-module-imports = true
  
  [tool.ruff.lint.per-file-ignores]
  ".github/*" = [
    "T201", # print
  ]
  "tests/*" = [
    "FBT001", # boolean-positional-arg-in-function-definition: allowed in `pytest.param()`
    "FBT003", # boolean-positional-value-in-function-call:     allowed test parameters
  ]
  
  [tool.ruff.lint.flake8-comprehensions]
  allow-dict-calls-with-keyword-arguments = true # code like `dict(a=1, b=2)` is allowed
  
  [tool.ruff.lint.isort]
  known-first-party = ["unblob"]
  
  [tool.ruff.lint.mccabe]
  max-complexity = 8
  
  [tool.ruff.lint.pylint]
  allow-magic-value-types = [
    "bytes",
    "int",   # bunch of ad-hoc bitmask values
    "str",
  ]
  
  [tool.ruff.lint.flake8-pytest-style]
  fixture-parentheses = false
  mark-parentheses = false
  parametrize-names-type = "csv"
  
  [tool.ruff.lint.flake8-tidy-imports.banned-api]
  "attr".msg = "Use `attrs` (with an 's') instead"
  
  [tool.pytest.ini_options]
  addopts = "--cov=unblob --cov=tests --cov-branch --cov-fail-under=90"
  norecursedirs = """
    *.egg
    *_extract
    .*
    dist
    build
    target
    tests/integration
  """
  
  [tool.coverage.paths]
  source = ["tests", "unblob"]
  
  [tool.coverage.run]
  branch = true
  concurrency = ["multiprocessing"]
  parallel = true
  sigterm = true
  source = ["tests", "unblob"]
  
  [tool.coverage.report]
  fail_under = 90
  skip_covered = true
  
  [tool.vulture]
  exclude = ["python/unblob/testing.py"]
  paths = ["python/", "vulture_whitelist.py"]
  
  [tool.pyright]
  exclude = [
    ".venv",
    "build",
  ]
  typeCheckingMode = "standard"
  
  
  [tool.maturin]
  locked = true
  manifest-path = "rust/Cargo.toml"
  module-name = "unblob._rust"
  python-packages = ["unblob"]
  python-source = "python"
  
  features = [
    "pyo3/abi3-py39",        # https://docs.python.org/3/c-api/stable.html
    "pyo3/extension-module", # This is an extension module
  ]
  
  [build-system]
  build-backend = "maturin"
  requires = ["maturin>=1.0,<2.0"]
  