From 5d99c9fc41dcc5eb59f529b8ee0cb8b6b57d1bfb Mon Sep 17 00:00:00 2001 From: zhiren Date: Wed, 6 Aug 2025 16:01:01 -0400 Subject: [PATCH 1/8] merged --- .pre-commit-config.yaml | 124 ++++++++++++++++++++-------------------- poetry.lock | 115 ++++++++++++++++++++++++++++++------- pyproject.toml | 2 +- 3 files changed, 158 insertions(+), 83 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 16a4348e..25fe1035 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,16 @@ repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-added-large-files - - id: check-docstring-first - - id: check-merge-conflict - - id: check-toml - - id: check-yaml - - id: double-quote-string-fixer - - id: end-of-file-fixer - - id: trailing-whitespace + # - repo: https://github.com/pre-commit/pre-commit-hooks + # rev: v4.3.0 + # hooks: + # - id: check-added-large-files + # - id: check-docstring-first + # - id: check-merge-conflict + # - id: check-toml + # - id: check-yaml + # - id: double-quote-string-fixer + # - id: end-of-file-fixer + # - id: trailing-whitespace - repo: https://github.com/psf/black rev: 22.8.0 @@ -21,57 +21,57 @@ repos: '--skip-string-normalization', ] - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - args: [ - '--line-length=120', - '--profile=black', - '--filter-files', - '--force-single-line-imports', - '--reverse-relative', - ] +# - repo: https://github.com/PyCQA/isort +# rev: 5.12.0 +# hooks: +# - id: isort +# args: [ +# '--line-length=120', +# '--profile=black', +# '--filter-files', +# '--force-single-line-imports', +# '--reverse-relative', +# ] - - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 - hooks: - - id: flake8 - additional_dependencies: [ - 'pycodestyle==2.9.1', # E,W - 'pyflakes==2.5.0', # F - 'mccabe==0.7.0', # C - 'flake8-bugbear==22.9.11', # B - 'flake8-builtins==1.5.3', # A - 'flake8-comprehensions==3.10.0', # C4 - 'flake8-debugger==4.1.2', # T1 - 'flake8-logging-format==0.7.5', # G - 'flake8-print==5.0.0', # T2 - ] - args: [ - '--select=E,W,F,C,B,A,C4,T1,G,T2', - '--ignore=E203,W503,B008,B305,A003,G004', - '--max-complexity=10', - '--max-line-length=120', - ] +# - repo: https://github.com/PyCQA/flake8 +# rev: 5.0.4 +# hooks: +# - id: flake8 +# additional_dependencies: [ +# 'pycodestyle==2.9.1', # E,W +# 'pyflakes==2.5.0', # F +# 'mccabe==0.7.0', # C +# 'flake8-bugbear==22.9.11', # B +# 'flake8-builtins==1.5.3', # A +# 'flake8-comprehensions==3.10.0', # C4 +# 'flake8-debugger==4.1.2', # T1 +# 'flake8-logging-format==0.7.5', # G +# 'flake8-print==5.0.0', # T2 +# ] +# args: [ +# '--select=E,W,F,C,B,A,C4,T1,G,T2', +# '--ignore=E203,W503,B008,B305,A003,G004', +# '--max-complexity=10', +# '--max-line-length=120', +# ] - # - repo: https://github.com/myint/docformatter - # rev: v1.5.0 - # hooks: - # - id: docformatter - # args: [ - # '--wrap-summaries=120', - # '--wrap-descriptions=120', - # '--in-place', - # ] +# # - repo: https://github.com/myint/docformatter +# # rev: v1.5.0 +# # hooks: +# # - id: docformatter +# # args: [ +# # '--wrap-summaries=120', +# # '--wrap-descriptions=120', +# # '--in-place', +# # ] - - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.4.2 - hooks: - - id: insert-license - files: \.py$ - args: [ - '--license-filepath=COPYRIGHT', - '--comment-style=#', - '--use-current-year', - ] +# - repo: https://github.com/Lucas-C/pre-commit-hooks +# rev: v1.4.2 +# hooks: +# - id: insert-license +# files: \.py$ +# args: [ +# '--license-filepath=COPYRIGHT', +# '--comment-style=#', +# '--use-current-year', +# ] diff --git a/poetry.lock b/poetry.lock index ecc3ccb6..cec63733 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. [[package]] name = "altgraph" @@ -6,6 +6,7 @@ version = "0.17.4" description = "Python graph (network) package" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, @@ -17,6 +18,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -28,6 +30,7 @@ version = "4.6.2.post1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, @@ -41,7 +44,7 @@ typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21.0b1) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""] trio = ["trio (>=0.26.1)"] [[package]] @@ -50,6 +53,8 @@ version = "1.4.1" description = "Atomic file writes." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main", "dev"] +markers = "sys_platform == \"win32\"" files = [ {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, ] @@ -60,18 +65,19 @@ version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] [[package]] name = "certifi" @@ -79,6 +85,7 @@ version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, @@ -90,6 +97,7 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -169,6 +177,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -180,6 +189,7 @@ version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, @@ -281,6 +291,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -295,10 +306,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\"", dev = "sys_platform == \"win32\""} [[package]] name = "coverage" @@ -306,6 +319,7 @@ version = "7.6.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, @@ -375,7 +389,7 @@ files = [ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "cryptography" @@ -383,6 +397,7 @@ version = "3.4.8" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "cryptography-3.4.8-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a00cf305f07b26c351d8d4e1af84ad7501eca8a342dedf24a7acb0e7b7406e14"}, {file = "cryptography-3.4.8-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:f44d141b8c4ea5eb4dbc9b3ad992d45580c1d22bf5e24363f2fbf50c2d7ae8a7"}, @@ -422,6 +437,7 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -433,6 +449,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -447,6 +465,7 @@ version = "18.9.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "Faker-18.9.0-py3-none-any.whl", hash = "sha256:defe9ed618a67ebf0f3eb1895e198c2355a7128a09087a6dce342ef2253263ea"}, {file = "Faker-18.9.0.tar.gz", hash = "sha256:80a5ea1464556c06b98bf47ea3adc7f33811a1182518d847860b1874080bd3c9"}, @@ -461,6 +480,7 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -469,7 +489,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "h11" @@ -477,6 +497,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -488,6 +509,7 @@ version = "0.16.3" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, @@ -509,6 +531,7 @@ version = "0.23.3" description = "The next generation HTTP client." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, @@ -521,7 +544,7 @@ rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} sniffio = "*" [package.extras] -brotli = ["brotli", "brotlicffi"] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -532,6 +555,7 @@ version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, @@ -546,6 +570,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -560,6 +585,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -571,6 +597,8 @@ version = "1.16.3" description = "Mach-O header analysis and editing" optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"darwin\"" files = [ {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, @@ -585,6 +613,7 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -596,6 +625,7 @@ version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, @@ -607,6 +637,8 @@ version = "2023.2.7" description = "Python PE parsing module" optional = false python-versions = ">=3.6.0" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, @@ -618,6 +650,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -634,6 +667,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -649,6 +683,7 @@ version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, @@ -667,6 +702,7 @@ version = "3.0.48" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" +groups = ["main"] files = [ {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, @@ -681,6 +717,7 @@ version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main", "dev"] files = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -692,6 +729,7 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -703,6 +741,7 @@ version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, @@ -715,7 +754,7 @@ typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and sys_platform == \"win32\""] [[package]] name = "pydantic-core" @@ -723,6 +762,7 @@ version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, @@ -824,6 +864,7 @@ version = "2.0.1" description = "Settings management using Pydantic" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pydantic_settings-2.0.1-py3-none-any.whl", hash = "sha256:579bbcbec3501e62bab73867b097ae10218201950e897463c98a182ffe7ed104"}, {file = "pydantic_settings-2.0.1.tar.gz", hash = "sha256:f440ec7cfb6dc63f03226c47b0e7803750d1b66a49ed944ac23eb4f0c84f8722"}, @@ -839,6 +880,7 @@ version = "6.11.1" description = "PyInstaller bundles a Python application and all its dependencies into a single package." optional = false python-versions = "<3.14,>=3.8" +groups = ["main"] files = [ {file = "pyinstaller-6.11.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:44e36172de326af6d4e7663b12f71dbd34e2e3e02233e181e457394423daaf03"}, {file = "pyinstaller-6.11.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6d12c45a29add78039066a53fb05967afaa09a672426072b13816fe7676abfc4"}, @@ -873,6 +915,7 @@ version = "2025.1" description = "Community maintained hooks for PyInstaller" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pyinstaller_hooks_contrib-2025.1-py3-none-any.whl", hash = "sha256:d3c799470cbc0bda60dcc8e6b4ab976777532b77621337f2037f558905e3a8e9"}, {file = "pyinstaller_hooks_contrib-2025.1.tar.gz", hash = "sha256:130818f9e9a0a7f2261f1fd66054966a3a50c99d000981c5d1db11d3ad0c6ab2"}, @@ -888,6 +931,7 @@ version = "2.9.0" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, @@ -905,6 +949,7 @@ version = "0.20220715.0" description = "Pure Python library for saving and loading PNG images" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"}, {file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"}, @@ -916,6 +961,7 @@ version = "6.2.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, @@ -940,6 +986,7 @@ version = "1.1.0" description = "Pytest plugin for Click" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytest_click-1.1.0-py3-none-any.whl", hash = "sha256:eade4742c2f02c345e78a32534a43e8db04acf98d415090539dacc880b7cd0e9"}, {file = "pytest_click-1.1.0.tar.gz", hash = "sha256:fdd9f6721f877dda021e7c5dc73e70aecd37e5ed23ec6820f8a7b3fd7b4f8d30"}, @@ -955,6 +1002,7 @@ version = "3.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, @@ -973,6 +1021,7 @@ version = "0.21.3" description = "Send responses to httpx." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest_httpx-0.21.3-py3-none-any.whl", hash = "sha256:50b52b910f6f6cfb0aa65039d6f5bedb6ae3a0c02a98c4a7187543fe437c428a"}, {file = "pytest_httpx-0.21.3.tar.gz", hash = "sha256:edcb62baceffbd57753c1a7afc4656b0e71e91c7a512e143c0adbac762d979c1"}, @@ -991,6 +1040,7 @@ version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, @@ -1008,6 +1058,7 @@ version = "1.1.0" description = "Randomise the order in which pytest tests are run with some control over the randomness" optional = false python-versions = ">=3.5.0" +groups = ["dev"] files = [ {file = "pytest-random-order-1.1.0.tar.gz", hash = "sha256:dbe6debb9353a7af984cc9eddbeb3577dd4dbbcc1529a79e3d21f68ed9b45605"}, {file = "pytest_random_order-1.1.0-py3-none-any.whl", hash = "sha256:6cb1e59ab0f798bb0c3488c11ae0c70d7d3340306a466d28b28ccd8ef8c20b7e"}, @@ -1022,6 +1073,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["dev"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -1036,6 +1088,7 @@ version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -1050,6 +1103,8 @@ version = "306" description = "Python for Window Extensions" optional = true python-versions = "*" +groups = ["main"] +markers = "extra == \"windows\"" files = [ {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, @@ -1073,6 +1128,8 @@ version = "0.2.3" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false python-versions = ">=3.6" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, @@ -1084,6 +1141,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1146,6 +1204,7 @@ version = "7.4.2" description = "QR Code image generator" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"}, {file = "qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"}, @@ -1169,6 +1228,7 @@ version = "1.10.0" description = "Python library to build pretty command line user prompts ⭐️" optional = false python-versions = ">=3.6,<4.0" +groups = ["main"] files = [ {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, @@ -1186,6 +1246,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1207,6 +1268,7 @@ version = "1.12.1" description = "Mock out responses from the requests package" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"}, {file = "requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563"}, @@ -1224,6 +1286,7 @@ version = "1.5.0" description = "Validating URI References per RFC 3986" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, @@ -1241,19 +1304,20 @@ version = "75.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"}, {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" @@ -1261,6 +1325,7 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1272,6 +1337,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -1283,6 +1349,7 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main", "dev"] files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -1294,6 +1361,8 @@ version = "2.0.2" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_full_version <= \"3.11.0a6\"" files = [ {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, @@ -1305,6 +1374,7 @@ version = "4.56.0" description = "Fast, Extensible Progress Meter" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +groups = ["main"] files = [ {file = "tqdm-4.56.0-py2.py3-none-any.whl", hash = "sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a"}, {file = "tqdm-4.56.0.tar.gz", hash = "sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65"}, @@ -1320,10 +1390,12 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +markers = {dev = "python_version == \"3.10\""} [[package]] name = "urllib3" @@ -1331,13 +1403,14 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1348,6 +1421,7 @@ version = "20.27.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, @@ -1360,7 +1434,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "wcwidth" @@ -1368,6 +1442,7 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -1377,6 +1452,6 @@ files = [ windows = ["pywin32"] [metadata] -lock-version = "2.0" -python-versions = ">=3.10,<3.11" -content-hash = "8743fc1b849d2f7b34660a2a575b06b895f13437b46c2beba2caf42ed694a11d" +lock-version = "2.1" +python-versions = ">=3.10,<3.13" +content-hash = "8697b730868cdb6b9cd147bd14486aca4ff7d612428401f080fc7c27b5473609" diff --git a/pyproject.toml b/pyproject.toml index 69d95428..9cae2622 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "This service is designed to support pilot platform" authors = ["Indoc Systems"] [tool.poetry.dependencies] -python = ">=3.10,<3.11" +python = ">=3.10,<3.13" questionary = "^1.10.0" tqdm = "4.56.0" cryptography = "3.4.8" From f846580299afe1cd00bd26da8f49f54f1734d225 Mon Sep 17 00:00:00 2001 From: zhiren Date: Tue, 20 Jan 2026 15:53:44 -0500 Subject: [PATCH 2/8] update duplicate logic to check based on item status --- .../file_manager/file_upload/file_upload.py | 70 +++++++++++++++---- .../file_manager/file_upload/upload_client.py | 32 ++++++--- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/app/services/file_manager/file_upload/file_upload.py b/app/services/file_manager/file_upload/file_upload.py index 5f4f510d..841bc350 100644 --- a/app/services/file_manager/file_upload/file_upload.py +++ b/app/services/file_manager/file_upload/file_upload.py @@ -11,6 +11,7 @@ from typing import Dict from typing import List from typing import Tuple +from uuid import uuid4 import click from click.exceptions import Abort @@ -121,8 +122,8 @@ def assemble_path( def item_duplication_check( - create_folder_flag: bool, file_objects: List[FileObject], upload_client: UploadClient -) -> List[FileObject]: + create_folder_flag: bool, file_objects: List[FileObject], upload_client: UploadClient, on_resume: bool = False +) -> Tuple[List[FileObject], List[Dict[str, Any]]]: ''' Summary: The function will check if the file is already uploaded on the platform. @@ -133,28 +134,40 @@ def item_duplication_check( - file_objects(List[FileObject]): the list of file object - upload_client(UploadClient): the upload client object Return: - - non_duplicate_file_objects(List[FileObject]): the list of file object that is not duplicated + - unregistered_items(List[FileObject]): the list of file object that is not duplicated + - [updated] registered_items(List[FileObject]): the list of file object that is already registered. + in conner case if preupload interrupted at specific batch, the local manifest + will mismatch with backend metadata. So we need to return the registered file objects as well. ''' # make the file duplication check to allow folde merging logger.info('Start checking file duplication') - non_duplicate_file_objects = [] + unregistered_items, registered_items = [], [] if create_folder_flag is True: - non_duplicate_file_objects = file_objects + unregistered_items = file_objects else: mhandler.SrvOutPutHandler.file_duplication_check() duplicated_file = [] debug_logger.debug(f'upload batch size: {AppConfig.Env.upload_batch_size}') for file_batchs in batch_generator(file_objects, batch_size=AppConfig.Env.upload_batch_size): start_time = time.time() - non_duplicates, duplicate_path = upload_client.check_upload_duplication(file_batchs) + non_duplicates, active_paths, registered_batch_items = upload_client.check_upload_duplication(file_batchs) debug_logger.debug(f'Check duplication time: {time.time() - start_time:.2f}s') - non_duplicate_file_objects.extend(non_duplicates) - duplicated_file.extend(duplicate_path) + unregistered_items.extend(non_duplicates) + duplicated_file.extend(active_paths) + registered_items.extend(registered_batch_items) + + # if normal upload we treat REGISTERED file as duplication + if not on_resume: + object_path = [ + file_object.get('parent_path', '') + '/' + file_object.get('name', '') + for file_object in registered_items + ] + duplicated_file.extend(object_path) # if all file objects we check are already existed on the platform # then we will exit the upload process - if len(non_duplicate_file_objects) == 0 and len(file_objects) != 0: + if len(unregistered_items) == 0 and len(file_objects) != 0 and on_resume is False: mhandler.SrvOutPutHandler.file_duplication_check_warning_with_all_same() SrvErrorHandler.customized_handle(ECustomizedError.UPLOAD_CANCEL, if_exit=True) elif len(duplicated_file) > 0: @@ -169,7 +182,7 @@ def item_duplication_check( mhandler.SrvOutPutHandler.cancel_upload() exit(1) - return non_duplicate_file_objects + return unregistered_items, registered_items def simple_upload( # noqa: C901 @@ -246,7 +259,7 @@ def simple_upload( # noqa: C901 file_objects.append(file_object) # make the file duplication check to allow folder merging - non_duplicate_file_objects = item_duplication_check(create_folder_flag, file_objects, upload_client) + non_duplicate_file_objects, _ = item_duplication_check(create_folder_flag, file_objects, upload_client) # here is list of pre upload result. We decided to call pre upload api by batch pre_upload_infos = [] @@ -393,7 +406,7 @@ def resume_upload( logger.info(f'Registered items: {len(unfinished_items)}') # make the file duplication check to allow folder merging - unregistered_items = manifest_json.get('unregistered_items') + unregistered_items_map = manifest_json.get('unregistered_items') unregistered_items = [ FileObject( object_path=value.get('object_path'), @@ -402,14 +415,41 @@ def resume_upload( job_id=value.get('job_id'), item_id=value.get('item_id'), ) - for _, value in unregistered_items.items() + for _, value in unregistered_items_map.items() ] - unregistered_items = item_duplication_check(False, unregistered_items, upload_client) + + # [updated] here duplication check api got update will filter out ACTIVE and REGISTERED items separately + # the reason is during the normal upload , there is a conner case that preupload got interrupted at specific batch + # so the local manifest will mismatch with backend metadata. Thus we need to return the registered file objects as well. + unregistered_items, unmatched_items = item_duplication_check( + False, unregistered_items, upload_client, on_resume=True + ) + # now create FileObject with local path + for item in unmatched_items: + object_path = item.get('parent_path', '') + '/' + item.get('name', '') + local_path = ( + unregistered_items_map.get(object_path).get('local_path') if unregistered_items_map.get(object_path) else '' + ) + if not local_path: + logger.warning(f'Cannot find local path for registered item: {object_path}. Skip it.') + continue + + registered_file_object = FileObject( + object_path=object_path, + local_path=local_path, + item_id=item.get('id'), + resumable_id=item.get('upload_id'), + job_id=str(uuid4()), + ) + unfinished_items.append(registered_file_object) + + # also need to update local manifest to remove the duplicated files + resumable_manifest_file = manifest_json.get('resumable_manifest_file') + upload_client.output_manifest(unfinished_items, unregistered_items, resumable_manifest_file) logger.info(f'Unregistered items: {len(unregistered_items)}') # redo preupload again batch_count = 1 - resumable_manifest_file = manifest_json.get('resumable_manifest_file') for file_batchs in batch_generator(unregistered_items, batch_size=AppConfig.Env.upload_batch_size): unfinished_items.extend(upload_client.pre_upload(file_batchs)) upload_client.output_manifest(unfinished_items, unregistered_items[batch_count + 1 :], resumable_manifest_file) diff --git a/app/services/file_manager/file_upload/upload_client.py b/app/services/file_manager/file_upload/upload_client.py index 704bf1ae..faa58c1f 100644 --- a/app/services/file_manager/file_upload/upload_client.py +++ b/app/services/file_manager/file_upload/upload_client.py @@ -158,7 +158,9 @@ def resume_upload(self, unfinished_file_objects: List[FileObject]) -> List[FileO return unfinished_file_objects @require_valid_token() - def check_upload_duplication(self, file_objects: List[FileObject]) -> Tuple[List[FileObject], List[str]]: + def check_upload_duplication( + self, file_objects: List[FileObject] + ) -> Tuple[List[FileObject], List[str], List[Dict[str, Any]]]: """ Summary: The function will call the api to check if the file has been uploaded. @@ -168,6 +170,9 @@ def check_upload_duplication(self, file_objects: List[FileObject]) -> Tuple[List return: - non_exist_file_objects(List[FileObject]): the file that need to be uploaded. - exist_files(List[str]): the file that has been uploaded. will be skipped + - [updated] reigstered_file_objects(List[Dict[str, Any]]): this is to handle the conner case + where upload interrupted at specific batch. The local json manifest mismatches with + backend metadata. So we need to return the registered file objects as well if possible. """ # generate a list of locations for uploaded files to check duplication @@ -182,8 +187,8 @@ def check_upload_duplication(self, file_objects: List[FileObject]) -> Tuple[List 'zone': 0 if self.zone == 'greenroom' else 1, } try: - self.endpoint = AppConfig.Connections.url_base + '/portal/v1' - response = self._post('files/exists', json=payload) + self.endpoint = AppConfig.Connections.url_bff + '/v2' + response = self._post('items/batch/exists', json=payload) except HTTPStatusError as e: response = e.response if response.status_code == 403: @@ -191,11 +196,16 @@ def check_upload_duplication(self, file_objects: List[FileObject]) -> Tuple[List else: SrvErrorHandler.default_handle('Error when checking file duplication', True) - # pop the file object if the file has been uploaded - # return the file objects that need to be uploaded - exist_files = response.json().get('result', []) - for exist_file_path in exist_files: - object_path_file_object_map.pop(exist_file_path.lower()) + # filter out the ACTIVE items and REGISTERED items from return + exist_items = response.json().get('result', []) + active_path, registered_items = [], [] + for item in exist_items: + object_path = item.get('parent_path', '') + '/' + item.get('name', '') + object_path_file_object_map.pop(object_path.lower(), None) + if item.get('status') == ItemStatus.ACTIVE: + active_path.append(object_path) + else: + registered_items.append(item) # reconstruct non exist file objects which will be uploaded # without lower() function. @@ -203,7 +213,7 @@ def check_upload_duplication(self, file_objects: List[FileObject]) -> Tuple[List for _, item in object_path_file_object_map.items(): return_list.update({item.object_path: item}) - return list(object_path_file_object_map.values()), exist_files + return list(object_path_file_object_map.values()), active_path, registered_items @require_valid_token() def pre_upload(self, file_objects: List[FileObject]) -> List[FileObject]: @@ -294,7 +304,9 @@ def output_manifest( 'current_folder_node': self.current_folder_node, 'tags': self.tags, 'registered_items': {file_object.item_id: file_object.to_dict() for file_object in registered_items}, - 'unregistered_items': {file_object.local_path: file_object.to_dict() for file_object in unregistered_items}, + 'unregistered_items': { + file_object.object_path: file_object.to_dict() for file_object in unregistered_items + }, 'attributes': self.attributes if self.attributes else {}, 'resumable_manifest_file': output_path, } From 382032befdb804d23bffc8ad48be87252180e301 Mon Sep 17 00:00:00 2001 From: zhiren Date: Tue, 20 Jan 2026 16:02:14 -0500 Subject: [PATCH 3/8] update COPYRIGHT --- .pre-commit-config.yaml | 111 ++++++++---------- app/__init__.py | 2 +- app/commands/__init__.py | 2 +- app/commands/container_registry.py | 2 +- app/commands/dataset.py | 2 +- app/commands/entry_point.py | 35 +++--- app/commands/file.py | 43 ++++--- app/commands/folder.py | 13 +- app/commands/project.py | 2 +- app/commands/user.py | 16 +-- app/configs/__init__.py | 2 +- app/configs/app_config.py | 2 +- app/configs/config.py | 5 +- app/configs/user_config.py | 24 ++-- app/configs/utils.py | 2 +- app/models/__init__.py | 2 +- app/models/enums.py | 2 +- app/models/item.py | 2 +- app/models/service_meta_class.py | 2 +- app/models/singleton.py | 2 +- app/models/upload_form.py | 2 +- app/pilotcli.py | 9 +- app/resources/custom_error.py | 2 +- app/resources/custom_help.py | 2 +- app/services/__init__.py | 2 +- app/services/clients/__init__.py | 2 +- app/services/clients/base_auth_client.py | 5 +- app/services/clients/base_client.py | 10 +- .../container_registry_manager.py | 5 +- app/services/crypto/__init__.py | 2 +- app/services/crypto/crypto.py | 2 +- .../dataset_manager/dataset_detail.py | 5 +- .../dataset_manager/dataset_download.py | 8 +- app/services/dataset_manager/dataset_list.py | 10 +- app/services/dataset_manager/model.py | 2 +- app/services/file_manager/__init__.py | 2 +- .../file_manager/file_download/__init__.py | 2 +- .../file_download/download_client.py | 11 +- .../file_manager/file_download/model.py | 2 +- app/services/file_manager/file_list.py | 8 +- app/services/file_manager/file_manifests.py | 5 +- .../file_manager/file_metadata/__init__.py | 2 +- .../file_metadata/file_metadata_client.py | 24 ++-- .../file_metadata/folder_client.py | 5 +- .../file_manager/file_move/__init__.py | 2 +- .../file_move/file_move_client.py | 19 ++- app/services/file_manager/file_tag.py | 6 +- .../file_manager/file_trash/__init__.py | 2 +- .../file_trash/file_trash_client.py | 12 +- app/services/file_manager/file_trash/utils.py | 12 +- .../file_manager/file_upload/__init__.py | 2 +- .../file_manager/file_upload/exception.py | 4 +- .../file_manager/file_upload/file_upload.py | 32 ++--- .../file_manager/file_upload/models.py | 10 +- .../file_manager/file_upload/upload_client.py | 17 +-- .../file_upload/upload_validator.py | 9 +- app/services/logger_services/__init__.py | 2 +- app/services/logger_services/debugging_log.py | 2 +- app/services/logger_services/log_functions.py | 2 +- app/services/output_manager/__init__.py | 2 +- app/services/output_manager/error_handler.py | 2 +- app/services/output_manager/help_page.py | 2 +- .../output_manager/message_handler.py | 2 +- app/services/project_manager/__init__.py | 2 +- app/services/project_manager/project.py | 8 +- app/services/user_authentication/__init__.py | 2 +- app/services/user_authentication/decorator.py | 8 +- .../user_authentication/token_manager.py | 5 +- .../user_authentication/user_login_logout.py | 10 +- app/utils/__init__.py | 2 +- app/utils/aggregated.py | 16 +-- tests/__init__.py | 2 +- tests/app/commands/test_dataset.py | 6 +- tests/app/commands/test_entry_point.py | 32 +++-- tests/app/commands/test_file.py | 21 ++-- tests/app/commands/test_folder.py | 2 +- tests/app/commands/test_project_command.py | 2 +- tests/app/commands/test_user.py | 2 +- tests/app/configs/__init__.py | 2 +- tests/app/configs/test_user_config.py | 5 +- .../dataset_manager/test_dataset_detail.py | 5 +- .../dataset_manager/test_dataset_download.py | 2 +- .../dataset_manager/test_dataset_list.py | 2 +- .../test_file_download_client.py | 7 +- .../test_file_metadata_client.py | 2 +- .../file_metadata/test_folder_client.py | 2 +- .../file_move/test_file_move_client.py | 2 +- .../file_trash/test_file_trash_client.py | 5 +- .../file_upload/test_file_upload.py | 18 +-- .../file_manager/file_upload/test_model.py | 2 +- .../file_upload/test_upload_client.py | 2 +- .../services/file_manager/test_manifest.py | 2 +- .../services/project_manager/test_project.py | 2 +- .../user_authentication/test_token_manager.py | 2 +- .../test_user_login_logout.py | 10 +- tests/app/utils/test_aggregated.py | 20 ++-- tests/conftest.py | 5 +- tests/fixtures/__init__.py | 2 +- tests/fixtures/fake.py | 2 +- 99 files changed, 323 insertions(+), 441 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 25fe1035..8d76a910 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,19 +1,16 @@ repos: - - # - repo: https://github.com/pre-commit/pre-commit-hooks - # rev: v4.3.0 - # hooks: - # - id: check-added-large-files - # - id: check-docstring-first - # - id: check-merge-conflict - # - id: check-toml - # - id: check-yaml - # - id: double-quote-string-fixer - # - id: end-of-file-fixer - # - id: trailing-whitespace + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-added-large-files + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 22.8.0 + rev: 26.1.0 hooks: - id: black args: [ @@ -21,57 +18,41 @@ repos: '--skip-string-normalization', ] -# - repo: https://github.com/PyCQA/isort -# rev: 5.12.0 -# hooks: -# - id: isort -# args: [ -# '--line-length=120', -# '--profile=black', -# '--filter-files', -# '--force-single-line-imports', -# '--reverse-relative', -# ] - -# - repo: https://github.com/PyCQA/flake8 -# rev: 5.0.4 -# hooks: -# - id: flake8 -# additional_dependencies: [ -# 'pycodestyle==2.9.1', # E,W -# 'pyflakes==2.5.0', # F -# 'mccabe==0.7.0', # C -# 'flake8-bugbear==22.9.11', # B -# 'flake8-builtins==1.5.3', # A -# 'flake8-comprehensions==3.10.0', # C4 -# 'flake8-debugger==4.1.2', # T1 -# 'flake8-logging-format==0.7.5', # G -# 'flake8-print==5.0.0', # T2 -# ] -# args: [ -# '--select=E,W,F,C,B,A,C4,T1,G,T2', -# '--ignore=E203,W503,B008,B305,A003,G004', -# '--max-complexity=10', -# '--max-line-length=120', -# ] + - repo: https://github.com/PyCQA/isort + rev: 7.0.0 + hooks: + - id: isort + args: [ + '--line-length=120', + '--profile=black', + '--filter-files', + ] -# # - repo: https://github.com/myint/docformatter -# # rev: v1.5.0 -# # hooks: -# # - id: docformatter -# # args: [ -# # '--wrap-summaries=120', -# # '--wrap-descriptions=120', -# # '--in-place', -# # ] + - repo: https://github.com/PyCQA/flake8 + rev: 7.3.0 + hooks: + - id: flake8 + additional_dependencies: [ + 'flake8-bugbear', # B + 'flake8-builtins', # A + 'flake8-comprehensions', # C4 + 'flake8-debugger', # T1 + 'flake8-print', # T2 + ] + args: [ + '--select=E,W,F,C,B,A,C4,T1,T2', + '--ignore=E203,W503,B008,B305,A003', + '--max-complexity=10', + '--max-line-length=120', + ] -# - repo: https://github.com/Lucas-C/pre-commit-hooks -# rev: v1.4.2 -# hooks: -# - id: insert-license -# files: \.py$ -# args: [ -# '--license-filepath=COPYRIGHT', -# '--comment-style=#', -# '--use-current-year', -# ] + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.6 + hooks: + - id: insert-license + files: \.py$ + args: [ + '--license-filepath=COPYRIGHT', + '--comment-style=#', + '--use-current-year', + ] diff --git a/app/__init__.py b/app/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/commands/__init__.py b/app/commands/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/commands/__init__.py +++ b/app/commands/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/commands/container_registry.py b/app/commands/container_registry.py index f554b695..20600bd7 100644 --- a/app/commands/container_registry.py +++ b/app/commands/container_registry.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/commands/dataset.py b/app/commands/dataset.py index 1ad5f53d..29f314af 100644 --- a/app/commands/dataset.py +++ b/app/commands/dataset.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/commands/entry_point.py b/app/commands/entry_point.py index 289016ef..587ea31c 100644 --- a/app/commands/entry_point.py +++ b/app/commands/entry_point.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -8,29 +8,24 @@ from app.services.user_authentication.decorator import require_login_session -from .container_registry import create_project -from .container_registry import get_secret -from .container_registry import invite_member -from .container_registry import list_projects -from .container_registry import list_repositories -from .dataset import dataset_download -from .dataset import dataset_list -from .dataset import dataset_show_detail -from .file import file_check_manifest -from .file import file_download -from .file import file_export_manifest -from .file import file_list -from .file import file_metadata_download -from .file import file_move -from .file import file_put -from .file import file_resume -from .file import file_trash +from .container_registry import create_project, get_secret, invite_member, list_projects, list_repositories +from .dataset import dataset_download, dataset_list, dataset_show_detail +from .file import ( + file_check_manifest, + file_download, + file_export_manifest, + file_list, + file_metadata_download, + file_move, + file_put, + file_resume, + file_trash, +) from .folder import folder_create # Import custom commands from .project import project_list_all -from .user import login -from .user import logout +from .user import login, logout container_registry_enabled = os.environ.get('PILOT_CLI_CONTAINER_REGISTRY_ENABLED', 'false') == 'true' diff --git a/app/commands/file.py b/app/commands/file.py index 7ebc1f1c..611d8716 100644 --- a/app/commands/file.py +++ b/app/commands/file.py @@ -1,11 +1,11 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import ast import json import os -from sys import exit +import sys import click from click.exceptions import Abort @@ -13,8 +13,7 @@ import app.services.output_manager.help_page as file_help import app.services.output_manager.message_handler as message_handler from app.configs.app_config import AppConfig -from app.models.item import ItemStatus -from app.models.item import ItemType +from app.models.item import ItemStatus, ItemType from app.services.file_manager.file_download.download_client import SrvFileDownload from app.services.file_manager.file_list import SrvFileList from app.services.file_manager.file_manifests import SrvFileManifests @@ -22,24 +21,22 @@ from app.services.file_manager.file_move.file_move_client import FileMoveClient from app.services.file_manager.file_trash.file_trash_client import FileTrashClient from app.services.file_manager.file_trash.utils import parse_trash_paths -from app.services.file_manager.file_upload.file_upload import assemble_path -from app.services.file_manager.file_upload.file_upload import resume_upload -from app.services.file_manager.file_upload.file_upload import simple_upload +from app.services.file_manager.file_upload.file_upload import assemble_path, resume_upload, simple_upload from app.services.file_manager.file_upload.upload_validator import UploadEventValidator from app.services.logger_services.debugging_log import debug_logger -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.output_manager.error_handler import customized_error_msg +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler, customized_error_msg from app.services.user_authentication.decorator import require_valid_token -from app.utils.aggregated import doc -from app.utils.aggregated import fit_terminal_width -from app.utils.aggregated import get_file_info_by_geid -from app.utils.aggregated import get_zone -from app.utils.aggregated import identify_target_folder -from app.utils.aggregated import normalize_input_paths -from app.utils.aggregated import normalize_join -from app.utils.aggregated import remove_the_output_file -from app.utils.aggregated import search_item +from app.utils.aggregated import ( + doc, + fit_terminal_width, + get_file_info_by_geid, + get_zone, + identify_target_folder, + normalize_input_paths, + normalize_join, + remove_the_output_file, + search_item, +) @click.command() @@ -159,7 +156,7 @@ def file_put(**kwargs): # noqa: C901 pass except Abort: message_handler.SrvOutPutHandler.cancel_upload() - exit(1) + sys.exit(1) # check if user input at least one file/folder if len(files) == 0: @@ -173,7 +170,7 @@ def file_put(**kwargs): # noqa: C901 ) except Abort: message_handler.SrvOutPutHandler.cancel_upload() - exit(1) + sys.exit(1) project_code, folder_type, target_folder = identify_target_folder(project_path) upload_val_event = { @@ -533,12 +530,12 @@ def file_move(**kwargs): message_handler.SrvOutPutHandler.move_action_failed( src_item_path, dest_item_path, 'Cannot move files between different projects' ) - exit(1) + sys.exit(1) elif len(src_item.split('/')) <= 2 or len(dest_item.split('/')) <= 1: message_handler.SrvOutPutHandler.move_action_failed( src_item_path, dest_item_path, 'Cannot move root/name/shared folders' ) - exit(1) + sys.exit(1) # tranlate keyword to correct object path src_keyword, src_path = src_item.split('/', 1) diff --git a/app/commands/folder.py b/app/commands/folder.py index f5e3b5bd..4eba6087 100644 --- a/app/commands/folder.py +++ b/app/commands/folder.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -7,14 +7,9 @@ import app.services.output_manager.message_handler as message_handler from app.configs.app_config import AppConfig from app.services.file_manager.file_metadata.folder_client import FolderClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.output_manager.help_page import FileHELP -from app.services.output_manager.help_page import FolderHelp -from app.services.output_manager.help_page import file_help_page -from app.services.output_manager.help_page import folder_help_page -from app.utils.aggregated import doc -from app.utils.aggregated import validate_folder_name +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler +from app.services.output_manager.help_page import FileHELP, FolderHelp, file_help_page, folder_help_page +from app.utils.aggregated import doc, validate_folder_name @click.command() diff --git a/app/commands/project.py b/app/commands/project.py index 496aa8c9..58aeb149 100644 --- a/app/commands/project.py +++ b/app/commands/project.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/commands/user.py b/app/commands/user.py index ec55ebee..8d145596 100644 --- a/app/commands/user.py +++ b/app/commands/user.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -13,13 +13,13 @@ import app.services.output_manager.message_handler as mhandler from app.models.enums import LoginMethod from app.services.user_authentication.decorator import require_login_session -from app.services.user_authentication.user_login_logout import login_using_api_key -from app.services.user_authentication.user_login_logout import user_device_id_login -from app.services.user_authentication.user_login_logout import user_logout -from app.services.user_authentication.user_login_logout import validate_user_device_login -from app.utils.aggregated import doc -from app.utils.aggregated import get_latest_cli_version -from app.utils.aggregated import get_version_compatibility +from app.services.user_authentication.user_login_logout import ( + login_using_api_key, + user_device_id_login, + user_logout, + validate_user_device_login, +) +from app.utils.aggregated import doc, get_latest_cli_version, get_version_compatibility @click.command() diff --git a/app/configs/__init__.py b/app/configs/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/configs/__init__.py +++ b/app/configs/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/configs/app_config.py b/app/configs/app_config.py index b9f19999..adeec17d 100644 --- a/app/configs/app_config.py +++ b/app/configs/app_config.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/configs/config.py b/app/configs/config.py index d0bef0bc..4ec2f8b2 100644 --- a/app/configs/config.py +++ b/app/configs/config.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -8,8 +8,7 @@ from typing import Set from pydantic import computed_field -from pydantic_settings import BaseSettings -from pydantic_settings import SettingsConfigDict +from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): diff --git a/app/configs/user_config.py b/app/configs/user_config.py index ecf10dd7..05dbe2e8 100644 --- a/app/configs/user_config.py +++ b/app/configs/user_config.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -8,21 +8,19 @@ import time from enum import IntEnum from pathlib import Path -from typing import Iterable -from typing import Union +from typing import Iterable, Union from app.configs.config import ConfigClass -from app.configs.utils import check_owner_linux -from app.configs.utils import check_owner_windows -from app.configs.utils import check_user_permission_linux -from app.configs.utils import check_user_permission_windows -from app.configs.utils import create_directory_with_permissions_windows +from app.configs.utils import ( + check_owner_linux, + check_owner_windows, + check_user_permission_linux, + check_user_permission_windows, + create_directory_with_permissions_windows, +) from app.models.singleton import Singleton -from app.services.crypto.crypto import decryption -from app.services.crypto.crypto import encryption -from app.services.crypto.crypto import generate_secret -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.crypto.crypto import decryption, encryption, generate_secret +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler class FilePermissions(IntEnum): diff --git a/app/configs/utils.py b/app/configs/utils.py index 265f61da..390ffe01 100644 --- a/app/configs/utils.py +++ b/app/configs/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/models/__init__.py b/app/models/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/models/enums.py b/app/models/enums.py index 626c4be9..9eaa2c47 100644 --- a/app/models/enums.py +++ b/app/models/enums.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023-2025 Indoc Systems +# Copyright (C) 2023-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/models/item.py b/app/models/item.py index 0e9454e4..3edf04f1 100644 --- a/app/models/item.py +++ b/app/models/item.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023-2025 Indoc Systems +# Copyright (C) 2023-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/models/service_meta_class.py b/app/models/service_meta_class.py index 8c556bd0..c7b2b303 100644 --- a/app/models/service_meta_class.py +++ b/app/models/service_meta_class.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/models/singleton.py b/app/models/singleton.py index e24f7962..6f149bff 100644 --- a/app/models/singleton.py +++ b/app/models/singleton.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/models/upload_form.py b/app/models/upload_form.py index d259f0a2..5487d1d7 100644 --- a/app/models/upload_form.py +++ b/app/models/upload_form.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/pilotcli.py b/app/pilotcli.py index 8ebf5be3..eb6b3057 100644 --- a/app/pilotcli.py +++ b/app/pilotcli.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -11,12 +11,9 @@ import app.services.output_manager.error_handler as error_handler import app.services.output_manager.message_handler as mhandler -from app.commands.entry_point import command_groups -from app.commands.entry_point import entry_point +from app.commands.entry_point import command_groups, entry_point from app.services.output_manager.help_page import get_cli_help_message -from app.utils.aggregated import doc -from app.utils.aggregated import get_latest_cli_version -from app.utils.aggregated import get_version_compatibility +from app.utils.aggregated import doc, get_latest_cli_version, get_version_compatibility class CustomHelpFormatter(HelpFormatter): diff --git a/app/resources/custom_error.py b/app/resources/custom_error.py index 82eef5ba..e9a44483 100644 --- a/app/resources/custom_error.py +++ b/app/resources/custom_error.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/resources/custom_help.py b/app/resources/custom_help.py index 4e675ddd..2675baa6 100644 --- a/app/resources/custom_help.py +++ b/app/resources/custom_help.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/__init__.py b/app/services/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/__init__.py +++ b/app/services/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/clients/__init__.py b/app/services/clients/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/clients/__init__.py +++ b/app/services/clients/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/clients/base_auth_client.py b/app/services/clients/base_auth_client.py index 01c47b68..d2e84a6f 100644 --- a/app/services/clients/base_auth_client.py +++ b/app/services/clients/base_auth_client.py @@ -1,10 +1,9 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import time -from typing import Any -from typing import Mapping +from typing import Any, Mapping from httpx import Response diff --git a/app/services/clients/base_client.py b/app/services/clients/base_client.py index 5cc23683..ac5f0efe 100644 --- a/app/services/clients/base_client.py +++ b/app/services/clients/base_client.py @@ -1,16 +1,12 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import logging import time -from typing import Any -from typing import Mapping -from typing import Optional +from typing import Any, Mapping, Optional -from httpx import Client -from httpx import RequestError -from httpx import Response +from httpx import Client, RequestError, Response from app.configs.config import ConfigClass diff --git a/app/services/container_registry_manager/container_registry_manager.py b/app/services/container_registry_manager/container_registry_manager.py index af495d0f..7ce3efa3 100644 --- a/app/services/container_registry_manager/container_registry_manager.py +++ b/app/services/container_registry_manager/container_registry_manager.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -9,8 +9,7 @@ from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig from app.models.service_meta_class import MetaService -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.user_authentication.decorator import require_valid_token diff --git a/app/services/crypto/__init__.py b/app/services/crypto/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/crypto/__init__.py +++ b/app/services/crypto/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/crypto/crypto.py b/app/services/crypto/crypto.py index f156f7c5..08014daf 100644 --- a/app/services/crypto/crypto.py +++ b/app/services/crypto/crypto.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/dataset_manager/dataset_detail.py b/app/services/dataset_manager/dataset_detail.py index 1e2cf3cd..012ce7b4 100644 --- a/app/services/dataset_manager/dataset_detail.py +++ b/app/services/dataset_manager/dataset_detail.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -9,8 +9,7 @@ from app.configs.user_config import UserConfig from app.models.service_meta_class import MetaService from app.services.clients.base_auth_client import BaseAuthClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from ..user_authentication.decorator import require_valid_token diff --git a/app/services/dataset_manager/dataset_download.py b/app/services/dataset_manager/dataset_download.py index 4aa3c26c..94d2dea0 100644 --- a/app/services/dataset_manager/dataset_download.py +++ b/app/services/dataset_manager/dataset_download.py @@ -1,12 +1,11 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import datetime import os import time -from typing import Any -from typing import Dict +from typing import Any, Dict import httpx from httpx import HTTPStatusError @@ -18,8 +17,7 @@ from app.models.service_meta_class import MetaService from app.services.clients.base_auth_client import BaseAuthClient from app.services.dataset_manager.model import EFileStatus -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.output_manager.message_handler import SrvOutPutHandler from ..user_authentication.decorator import require_valid_token diff --git a/app/services/dataset_manager/dataset_list.py b/app/services/dataset_manager/dataset_list.py index db6f5f46..959cca90 100644 --- a/app/services/dataset_manager/dataset_list.py +++ b/app/services/dataset_manager/dataset_list.py @@ -1,11 +1,8 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. -from typing import Any -from typing import Dict -from typing import List -from typing import Tuple +from typing import Any, Dict, List, Tuple from httpx import HTTPStatusError @@ -13,8 +10,7 @@ from app.configs.user_config import UserConfig from app.models.service_meta_class import MetaService from app.services.clients.base_auth_client import BaseAuthClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.output_manager.message_handler import SrvOutPutHandler from ..user_authentication.decorator import require_valid_token diff --git a/app/services/dataset_manager/model.py b/app/services/dataset_manager/model.py index c50ece93..dd15fbb1 100644 --- a/app/services/dataset_manager/model.py +++ b/app/services/dataset_manager/model.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/__init__.py b/app/services/file_manager/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/file_manager/__init__.py +++ b/app/services/file_manager/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_download/__init__.py b/app/services/file_manager/file_download/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/file_manager/file_download/__init__.py +++ b/app/services/file_manager/file_download/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_download/download_client.py b/app/services/file_manager/file_download/download_client.py index 02e9a151..bf4f0bd6 100644 --- a/app/services/file_manager/file_download/download_client.py +++ b/app/services/file_manager/file_download/download_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -19,8 +19,7 @@ from app.models.item import ItemZone from app.models.service_meta_class import MetaService from app.services.clients.base_auth_client import BaseAuthClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.user_authentication.decorator import require_valid_token from .model import EFileStatus @@ -54,12 +53,12 @@ def print_prepare_msg(self, message): finished_msg = message.replace('ing', 'ed') while True: if self.check_point: - click.secho(f"{finished_msg}{' '*space_width}\r", fg='white', nl=False) + click.secho(f"{finished_msg}{' ' * space_width}\r", fg='white', nl=False) break - click.secho(f"{message}{' '*space_width}\r", fg='white', nl=False) + click.secho(f"{message}{' ' * space_width}\r", fg='white', nl=False) for i in range(5): time.sleep(1) - click.secho(f"{message}{'.'*i}\r", fg='white', nl=False) + click.secho(f"{message}{'.' * i}\r", fg='white', nl=False) def get_download_url(self, zone): if zone == ItemZone.GREENROOM.value: diff --git a/app/services/file_manager/file_download/model.py b/app/services/file_manager/file_download/model.py index 87355185..6c750c14 100644 --- a/app/services/file_manager/file_download/model.py +++ b/app/services/file_manager/file_download/model.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_list.py b/app/services/file_manager/file_list.py index 5cd0518d..3d44d39e 100644 --- a/app/services/file_manager/file_list.py +++ b/app/services/file_manager/file_list.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -10,12 +10,10 @@ import app.services.logger_services.log_functions as logger from app.configs.app_config import AppConfig -from app.models.item import ItemStatus -from app.models.item import ItemType +from app.models.item import ItemStatus, ItemType from app.models.service_meta_class import MetaService from app.services.clients.base_auth_client import BaseAuthClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.user_authentication.decorator import require_valid_token from app.utils.aggregated import fit_terminal_width diff --git a/app/services/file_manager/file_manifests.py b/app/services/file_manager/file_manifests.py index d014281e..b7a3907f 100644 --- a/app/services/file_manager/file_manifests.py +++ b/app/services/file_manager/file_manifests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -9,8 +9,7 @@ from app.configs.user_config import UserConfig from app.models.service_meta_class import MetaService from app.services.clients.base_auth_client import BaseAuthClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler def dupe_checking_hook(pairs): diff --git a/app/services/file_manager/file_metadata/__init__.py b/app/services/file_manager/file_metadata/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/file_manager/file_metadata/__init__.py +++ b/app/services/file_manager/file_metadata/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_metadata/file_metadata_client.py b/app/services/file_manager/file_metadata/file_metadata_client.py index 977c02f8..2a5e6763 100644 --- a/app/services/file_manager/file_metadata/file_metadata_client.py +++ b/app/services/file_manager/file_metadata/file_metadata_client.py @@ -1,18 +1,12 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import json +import sys from os import makedirs -from os.path import basename -from os.path import dirname -from os.path import exists -from os.path import join -from sys import exit -from typing import Any -from typing import Dict -from typing import List -from typing import Union +from os.path import basename, dirname, exists, join +from typing import Any, Dict, List, Union import click from click.exceptions import Abort @@ -20,10 +14,8 @@ import app.services.logger_services.log_functions as logger import app.services.output_manager.message_handler as message_handler from app.models.item import ItemType -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import customized_error_msg -from app.utils.aggregated import get_attribute_template_by_id -from app.utils.aggregated import search_item +from app.services.output_manager.error_handler import ECustomizedError, customized_error_msg +from app.utils.aggregated import get_attribute_template_by_id, search_item class FileMetaClient: @@ -87,7 +79,7 @@ def _check_duplication(self, general_loc: str, attribute_loc: str, tag_loc: str) click.confirm(duplicate_error, abort=True) except Abort: message_handler.SrvOutPutHandler.cancel_metadata_download() - exit(1) + sys.exit(1) def save_file_metadata(self, file_loc: str, metadata: Union[dict, list]) -> None: """ @@ -116,7 +108,7 @@ def download_file_metadata(self) -> List[Dict[str, Any]]: item_res = search_item(project_code, self.zone, object_path) if item_res.get('code') == 404: logger.error(f'Cannot find item {self.file_path} at {self.zone}.') - exit(1) + sys.exit(1) # filter out item metadata item_res = item_res.get('result', {}) diff --git a/app/services/file_manager/file_metadata/folder_client.py b/app/services/file_manager/file_metadata/folder_client.py index ef95a48b..8b145351 100644 --- a/app/services/file_manager/file_metadata/folder_client.py +++ b/app/services/file_manager/file_metadata/folder_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -10,8 +10,7 @@ from app.services.clients.base_auth_client import BaseAuthClient from app.services.output_manager import message_handler from app.services.output_manager.error_handler import SrvErrorHandler -from app.utils.aggregated import check_item_duplication -from app.utils.aggregated import search_item +from app.utils.aggregated import check_item_duplication, search_item class FolderClient(BaseAuthClient): diff --git a/app/services/file_manager/file_move/__init__.py b/app/services/file_manager/file_move/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/file_manager/file_move/__init__.py +++ b/app/services/file_manager/file_move/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_move/file_move_client.py b/app/services/file_manager/file_move/file_move_client.py index c268dac1..f177e4e5 100644 --- a/app/services/file_manager/file_move/file_move_client.py +++ b/app/services/file_manager/file_move/file_move_client.py @@ -1,9 +1,9 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. +import sys import uuid -from sys import exit import click from click import Abort @@ -13,12 +13,9 @@ from app.configs.app_config import AppConfig from app.models.item import ItemType from app.services.clients.base_auth_client import BaseAuthClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.output_manager.error_handler import customized_error_msg +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler, customized_error_msg from app.services.user_authentication.decorator import require_valid_token -from app.utils.aggregated import check_item_duplication -from app.utils.aggregated import search_item +from app.utils.aggregated import check_item_duplication, search_item class FileMoveClient(BaseAuthClient): @@ -91,7 +88,7 @@ def create_object_path_if_not_exist(self, folder_path: str) -> dict: click.confirm(customized_error_msg(ECustomizedError.CREATE_FOLDER_IF_NOT_EXIST), abort=True) except Abort: message_handler.SrvOutPutHandler.move_cancelled() - exit(1) + sys.exit(1) else: return @@ -140,7 +137,7 @@ def move_file(self) -> None: message_handler.SrvOutPutHandler.move_action_failed( self.src_item_path, f'{self.dest_item_path}/{item_name}', f'Item {item_name} already exists' ) - exit(1) + sys.exit(1) else: self.dest_item_path = f'{self.dest_item_path}/{self.src_item_path.split("/")[-1]}' else: @@ -151,7 +148,7 @@ def move_file(self) -> None: message_handler.SrvOutPutHandler.move_action_failed( self.src_item_path, self.dest_item_path, f'Parent folder {parent_path} not exist' ) - exit(1) + sys.exit(1) try: payload = { @@ -174,4 +171,4 @@ def move_file(self) -> None: else: error_message = response.json().get('error_msg') message_handler.SrvOutPutHandler.move_action_failed(self.src_item_path, self.dest_item_path, error_message) - exit(1) + sys.exit(1) diff --git a/app/services/file_manager/file_tag.py b/app/services/file_manager/file_tag.py index 78277a0b..7b9f4e70 100644 --- a/app/services/file_manager/file_tag.py +++ b/app/services/file_manager/file_tag.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -9,9 +9,7 @@ from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig from app.models.service_meta_class import MetaService -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.output_manager.error_handler import customized_error_msg +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler, customized_error_msg from app.services.user_authentication.decorator import require_valid_token diff --git a/app/services/file_manager/file_trash/__init__.py b/app/services/file_manager/file_trash/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/file_manager/file_trash/__init__.py +++ b/app/services/file_manager/file_trash/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_trash/file_trash_client.py b/app/services/file_manager/file_trash/file_trash_client.py index b77d0d67..6c400265 100644 --- a/app/services/file_manager/file_trash/file_trash_client.py +++ b/app/services/file_manager/file_trash/file_trash_client.py @@ -1,11 +1,9 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import time -from typing import Any -from typing import Dict -from typing import List +from typing import Any, Dict, List from uuid import UUID from httpx import HTTPStatusError @@ -14,10 +12,8 @@ from app.models.item import ItemStatus from app.services.clients.base_auth_client import BaseAuthClient from app.services.output_manager import message_handler -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.utils.aggregated import get_file_info_by_geid -from app.utils.aggregated import get_zone +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler +from app.utils.aggregated import get_file_info_by_geid, get_zone class FileTrashClient(BaseAuthClient): diff --git a/app/services/file_manager/file_trash/utils.py b/app/services/file_manager/file_trash/utils.py index 93bef481..512a4900 100644 --- a/app/services/file_manager/file_trash/utils.py +++ b/app/services/file_manager/file_trash/utils.py @@ -1,15 +1,11 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. -from typing import Dict -from typing import List -from typing import Tuple +from typing import Dict, List, Tuple -from app.models.item import ItemStatus -from app.models.item import ItemType -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.models.item import ItemStatus, ItemType +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.utils.aggregated import search_item diff --git a/app/services/file_manager/file_upload/__init__.py b/app/services/file_manager/file_upload/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/file_manager/file_upload/__init__.py +++ b/app/services/file_manager/file_upload/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_upload/exception.py b/app/services/file_manager/file_upload/exception.py index c72f62ca..0d4df23b 100644 --- a/app/services/file_manager/file_upload/exception.py +++ b/app/services/file_manager/file_upload/exception.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -7,4 +7,6 @@ class INVALID_CHUNK_ETAG(Exception): chunk_number: int def __init__(self, chunk_number: int) -> None: + super().__init__(f'Invalid ETag for chunk number {chunk_number}') + self.chunk_number = chunk_number diff --git a/app/services/file_manager/file_upload/file_upload.py b/app/services/file_manager/file_upload/file_upload.py index 841bc350..3e554fa4 100644 --- a/app/services/file_manager/file_upload/file_upload.py +++ b/app/services/file_manager/file_upload/file_upload.py @@ -1,16 +1,13 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import os +import sys import time import zipfile from multiprocessing.pool import ThreadPool -from sys import exit -from typing import Any -from typing import Dict -from typing import List -from typing import Tuple +from typing import Any, Dict, List, Tuple from uuid import uuid4 import click @@ -20,19 +17,11 @@ import app.services.output_manager.message_handler as mhandler from app.configs.app_config import AppConfig from app.models.item import ItemType -from app.services.file_manager.file_upload.models import FileObject -from app.services.file_manager.file_upload.models import ItemStatus -from app.services.file_manager.file_upload.models import UploadType +from app.services.file_manager.file_upload.models import FileObject, ItemStatus, UploadType from app.services.file_manager.file_upload.upload_client import UploadClient from app.services.logger_services.debugging_log import debug_logger -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.output_manager.error_handler import customized_error_msg -from app.utils.aggregated import batch_generator -from app.utils.aggregated import get_file_in_folder -from app.utils.aggregated import get_file_info_by_geid -from app.utils.aggregated import normalize_join -from app.utils.aggregated import search_item +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler, customized_error_msg +from app.utils.aggregated import batch_generator, get_file_in_folder, get_file_info_by_geid, normalize_join, search_item def compress_folder_to_zip(path: str) -> str: @@ -104,7 +93,7 @@ def assemble_path( click.confirm(customized_error_msg(ECustomizedError.CREATE_FOLDER_IF_NOT_EXIST), abort=True) except Abort: mhandler.SrvOutPutHandler.cancel_upload() - exit(1) + sys.exit(1) # stop scaning and use the current folder as parent folder current_folder_node = folder_path @@ -180,7 +169,7 @@ def item_duplication_check( ) except Abort: mhandler.SrvOutPutHandler.cancel_upload() - exit(1) + sys.exit(1) return unregistered_items, registered_items @@ -419,8 +408,9 @@ def resume_upload( ] # [updated] here duplication check api got update will filter out ACTIVE and REGISTERED items separately - # the reason is during the normal upload , there is a conner case that preupload got interrupted at specific batch - # so the local manifest will mismatch with backend metadata. Thus we need to return the registered file objects as well. + # the reason is during the normal upload , there is a conner case that preupload got interrupted at + # specific batch so the local manifest will mismatch with backend metadata. Thus we need to return + # the registered file objects as well. unregistered_items, unmatched_items = item_duplication_check( False, unregistered_items, upload_client, on_resume=True ) diff --git a/app/services/file_manager/file_upload/models.py b/app/services/file_manager/file_upload/models.py index f2a069b1..4b3ff837 100644 --- a/app/services/file_manager/file_upload/models.py +++ b/app/services/file_manager/file_upload/models.py @@ -1,15 +1,11 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import math from enum import Enum -from os.path import basename -from os.path import dirname -from os.path import getsize -from typing import Any -from typing import Dict -from typing import Tuple +from os.path import basename, dirname, getsize +from typing import Any, Dict, Tuple from tqdm import tqdm diff --git a/app/services/file_manager/file_upload/upload_client.py b/app/services/file_manager/file_upload/upload_client.py index faa58c1f..b12673a6 100644 --- a/app/services/file_manager/file_upload/upload_client.py +++ b/app/services/file_manager/file_upload/upload_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -11,10 +11,7 @@ import time from logging import getLogger from multiprocessing.pool import ThreadPool -from typing import Any -from typing import Dict -from typing import List -from typing import Tuple +from typing import Any, Dict, List, Tuple from uuid import UUID import httpx @@ -25,14 +22,10 @@ from app.configs.user_config import UserConfig from app.models.upload_form import generate_on_success_form from app.services.clients.base_auth_client import BaseAuthClient -from app.services.file_manager.file_upload.models import FileObject -from app.services.file_manager.file_upload.models import UploadType -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.file_manager.file_upload.models import FileObject, UploadType +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.user_authentication.decorator import require_valid_token -from app.utils.aggregated import ItemStatus -from app.utils.aggregated import batch_generator -from app.utils.aggregated import get_file_info_by_geid +from app.utils.aggregated import ItemStatus, batch_generator, get_file_info_by_geid from .exception import INVALID_CHUNK_ETAG diff --git a/app/services/file_manager/file_upload/upload_validator.py b/app/services/file_manager/file_upload/upload_validator.py index 5bbee8be..15fd663c 100644 --- a/app/services/file_manager/file_upload/upload_validator.py +++ b/app/services/file_manager/file_upload/upload_validator.py @@ -1,15 +1,12 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. -from typing import Any -from typing import Dict -from typing import List +from typing import Any, Dict, List from app.services.file_manager.file_manifests import SrvFileManifests from app.services.file_manager.file_tag import SrvFileTag -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.utils.aggregated import search_item diff --git a/app/services/logger_services/__init__.py b/app/services/logger_services/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/logger_services/__init__.py +++ b/app/services/logger_services/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/logger_services/debugging_log.py b/app/services/logger_services/debugging_log.py index 872b9dd8..e6973d64 100644 --- a/app/services/logger_services/debugging_log.py +++ b/app/services/logger_services/debugging_log.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/logger_services/log_functions.py b/app/services/logger_services/log_functions.py index 1f150de7..50ec6771 100644 --- a/app/services/logger_services/log_functions.py +++ b/app/services/logger_services/log_functions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/output_manager/__init__.py b/app/services/output_manager/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/output_manager/__init__.py +++ b/app/services/output_manager/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/output_manager/error_handler.py b/app/services/output_manager/error_handler.py index f9f9da8a..c5f21a8a 100644 --- a/app/services/output_manager/error_handler.py +++ b/app/services/output_manager/error_handler.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/output_manager/help_page.py b/app/services/output_manager/help_page.py index 88911550..cf7ae4e1 100644 --- a/app/services/output_manager/help_page.py +++ b/app/services/output_manager/help_page.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/output_manager/message_handler.py b/app/services/output_manager/message_handler.py index 73148c0a..c8fb9783 100644 --- a/app/services/output_manager/message_handler.py +++ b/app/services/output_manager/message_handler.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/project_manager/__init__.py b/app/services/project_manager/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/project_manager/__init__.py +++ b/app/services/project_manager/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/project_manager/project.py b/app/services/project_manager/project.py index 237b15bd..e84b40bc 100644 --- a/app/services/project_manager/project.py +++ b/app/services/project_manager/project.py @@ -1,16 +1,14 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. -from typing import Dict -from typing import Tuple +from typing import Dict, Tuple from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig from app.models.service_meta_class import MetaService from app.services.clients.base_auth_client import BaseAuthClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.output_manager.message_handler import SrvOutPutHandler from ..user_authentication.decorator import require_valid_token diff --git a/app/services/user_authentication/__init__.py b/app/services/user_authentication/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/services/user_authentication/__init__.py +++ b/app/services/user_authentication/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/user_authentication/decorator.py b/app/services/user_authentication/decorator.py index ab0562f6..7ed365b1 100644 --- a/app/services/user_authentication/decorator.py +++ b/app/services/user_authentication/decorator.py @@ -1,15 +1,13 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. from functools import wraps from app.configs.config import ConfigClass -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.user_authentication.token_manager import SrvTokenManager -from app.services.user_authentication.user_login_logout import check_is_active -from app.services.user_authentication.user_login_logout import check_is_login +from app.services.user_authentication.user_login_logout import check_is_active, check_is_login def require_valid_token(azp=ConfigClass.keycloak_device_client_id): diff --git a/app/services/user_authentication/token_manager.py b/app/services/user_authentication/token_manager.py index 8340456c..7fe6bdcd 100644 --- a/app/services/user_authentication/token_manager.py +++ b/app/services/user_authentication/token_manager.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -12,8 +12,7 @@ from app.configs.user_config import UserConfig from app.models.service_meta_class import MetaService from app.services.clients.base_client import BaseClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.user_authentication.user_login_logout import login_using_api_key diff --git a/app/services/user_authentication/user_login_logout.py b/app/services/user_authentication/user_login_logout.py index 15a69af7..81d68ed5 100644 --- a/app/services/user_authentication/user_login_logout.py +++ b/app/services/user_authentication/user_login_logout.py @@ -1,12 +1,9 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import time -from typing import Any -from typing import Dict -from typing import Tuple -from typing import Union +from typing import Any, Dict, Tuple, Union from uuid import uuid4 import jwt @@ -16,8 +13,7 @@ from app.configs.config import ConfigClass from app.configs.user_config import UserConfig from app.services.clients.base_client import BaseClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.output_manager.message_handler import SrvOutPutHandler diff --git a/app/utils/__init__.py b/app/utils/__init__.py index 50fc3895..2dadda1a 100644 --- a/app/utils/__init__.py +++ b/app/utils/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/utils/aggregated.py b/app/utils/aggregated.py index bc33680f..456777b0 100644 --- a/app/utils/aggregated.py +++ b/app/utils/aggregated.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -7,23 +7,17 @@ import re import shutil import time -from typing import Any -from typing import Dict -from typing import List -from typing import Tuple +from typing import Any, Dict, List, Tuple from httpx import HTTPStatusError from packaging.version import Version import app.services.logger_services.log_functions as logger from app.configs.app_config import AppConfig -from app.models.item import ItemStatus -from app.models.item import ItemType -from app.services.clients.base_auth_client import BaseAuthClient -from app.services.clients.base_auth_client import BaseClient +from app.models.item import ItemStatus, ItemType +from app.services.clients.base_auth_client import BaseAuthClient, BaseClient from app.services.logger_services.debugging_log import debug_logger -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.output_manager.error_handler import ECustomizedError, SrvErrorHandler from app.services.user_authentication.decorator import require_valid_token diff --git a/tests/__init__.py b/tests/__init__.py index 50fc3895..2dadda1a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/commands/test_dataset.py b/tests/app/commands/test_dataset.py index 09f46eda..15f39ec7 100644 --- a/tests/app/commands/test_dataset.py +++ b/tests/app/commands/test_dataset.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -7,9 +7,7 @@ import pytest import questionary -from app.commands.dataset import dataset_download -from app.commands.dataset import dataset_list -from app.commands.dataset import dataset_show_detail +from app.commands.dataset import dataset_download, dataset_list, dataset_show_detail from app.configs.app_config import AppConfig diff --git a/tests/app/commands/test_entry_point.py b/tests/app/commands/test_entry_point.py index 19fb5985..c42c17cf 100644 --- a/tests/app/commands/test_entry_point.py +++ b/tests/app/commands/test_entry_point.py @@ -1,24 +1,22 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. -from app.commands.dataset import dataset_download -from app.commands.dataset import dataset_list -from app.commands.dataset import dataset_show_detail -from app.commands.entry_point import command_groups -from app.commands.entry_point import entry_point -from app.commands.file import file_check_manifest -from app.commands.file import file_download -from app.commands.file import file_export_manifest -from app.commands.file import file_list -from app.commands.file import file_metadata_download -from app.commands.file import file_move -from app.commands.file import file_put -from app.commands.file import file_resume -from app.commands.file import file_trash +from app.commands.dataset import dataset_download, dataset_list, dataset_show_detail +from app.commands.entry_point import command_groups, entry_point +from app.commands.file import ( + file_check_manifest, + file_download, + file_export_manifest, + file_list, + file_metadata_download, + file_move, + file_put, + file_resume, + file_trash, +) from app.commands.project import project_list_all -from app.commands.user import login -from app.commands.user import logout +from app.commands.user import login, logout def test_entry_point(): diff --git a/tests/app/commands/test_file.py b/tests/app/commands/test_file.py index 00e2455c..02a6ccde 100644 --- a/tests/app/commands/test_file.py +++ b/tests/app/commands/test_file.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -11,19 +11,20 @@ import pytest import questionary -from app.commands.file import file_download -from app.commands.file import file_list -from app.commands.file import file_metadata_download -from app.commands.file import file_move -from app.commands.file import file_put -from app.commands.file import file_resume -from app.commands.file import file_trash +from app.commands.file import ( + file_download, + file_list, + file_metadata_download, + file_move, + file_put, + file_resume, + file_trash, +) from app.configs.app_config import AppConfig from app.models.item import ItemType from app.services.file_manager.file_metadata.file_metadata_client import FileMetaClient from app.services.file_manager.file_upload.models import FileObject -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import customized_error_msg +from app.services.output_manager.error_handler import ECustomizedError, customized_error_msg from tests.conftest import decoded_token diff --git a/tests/app/commands/test_folder.py b/tests/app/commands/test_folder.py index 7a1471cf..791f2dba 100644 --- a/tests/app/commands/test_folder.py +++ b/tests/app/commands/test_folder.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/commands/test_project_command.py b/tests/app/commands/test_project_command.py index e4b54d2e..c7a99455 100644 --- a/tests/app/commands/test_project_command.py +++ b/tests/app/commands/test_project_command.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/commands/test_user.py b/tests/app/commands/test_user.py index d4af0b49..4f55ef4c 100644 --- a/tests/app/commands/test_user.py +++ b/tests/app/commands/test_user.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023-2025 Indoc Systems +# Copyright (C) 2023-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/configs/__init__.py b/tests/app/configs/__init__.py index 0886395d..c7720cf6 100644 --- a/tests/app/configs/__init__.py +++ b/tests/app/configs/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2023-2025 Indoc Systems +# Copyright (C) 2023-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/configs/test_user_config.py b/tests/app/configs/test_user_config.py index c55254c3..0d32fe2f 100644 --- a/tests/app/configs/test_user_config.py +++ b/tests/app/configs/test_user_config.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023-2025 Indoc Systems +# Copyright (C) 2023-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -8,8 +8,7 @@ import pytest -from app.configs.user_config import FilePermissions -from app.configs.user_config import UserConfig +from app.configs.user_config import FilePermissions, UserConfig @pytest.fixture diff --git a/tests/app/services/dataset_manager/test_dataset_detail.py b/tests/app/services/dataset_manager/test_dataset_detail.py index e597d9d2..0b640dcc 100644 --- a/tests/app/services/dataset_manager/test_dataset_detail.py +++ b/tests/app/services/dataset_manager/test_dataset_detail.py @@ -1,12 +1,11 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. import pytest from app.services.dataset_manager.dataset_detail import SrvDatasetDetailManager -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import customized_error_msg +from app.services.output_manager.error_handler import ECustomizedError, customized_error_msg test_dataset_code = 'test_code' diff --git a/tests/app/services/dataset_manager/test_dataset_download.py b/tests/app/services/dataset_manager/test_dataset_download.py index 7f655818..354960d1 100644 --- a/tests/app/services/dataset_manager/test_dataset_download.py +++ b/tests/app/services/dataset_manager/test_dataset_download.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/dataset_manager/test_dataset_list.py b/tests/app/services/dataset_manager/test_dataset_list.py index 1c0c8ef1..36d3fe10 100644 --- a/tests/app/services/dataset_manager/test_dataset_list.py +++ b/tests/app/services/dataset_manager/test_dataset_list.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/file_manager/file_download/test_file_download_client.py b/tests/app/services/file_manager/file_download/test_file_download_client.py index 58bb22ab..233c229f 100644 --- a/tests/app/services/file_manager/file_download/test_file_download_client.py +++ b/tests/app/services/file_manager/file_download/test_file_download_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -14,8 +14,7 @@ from app.models.item import ItemZone from app.services.file_manager.file_download.download_client import SrvFileDownload from app.services.file_manager.file_download.model import EFileStatus -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import customized_error_msg +from app.services.output_manager.error_handler import ECustomizedError, customized_error_msg from tests.conftest import decoded_token @@ -370,7 +369,7 @@ def test_base_client_timeout_logs_error_type(mocker): mock_logger = mocker.patch('app.services.clients.base_client.logger.error') - with pytest.raises(Exception): + with pytest.raises(Exception, match=r'Unable to query data.*ReadTimeout'): test_client.download_status() mock_logger.assert_called_once() diff --git a/tests/app/services/file_manager/file_metadata/test_file_metadata_client.py b/tests/app/services/file_manager/file_metadata/test_file_metadata_client.py index fc056e5e..a54624a2 100644 --- a/tests/app/services/file_manager/file_metadata/test_file_metadata_client.py +++ b/tests/app/services/file_manager/file_metadata/test_file_metadata_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/file_manager/file_metadata/test_folder_client.py b/tests/app/services/file_manager/file_metadata/test_folder_client.py index f4d862db..23c13cda 100644 --- a/tests/app/services/file_manager/file_metadata/test_folder_client.py +++ b/tests/app/services/file_manager/file_metadata/test_folder_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/file_manager/file_move/test_file_move_client.py b/tests/app/services/file_manager/file_move/test_file_move_client.py index 30c0e91f..b7032a38 100644 --- a/tests/app/services/file_manager/file_move/test_file_move_client.py +++ b/tests/app/services/file_manager/file_move/test_file_move_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/file_manager/file_trash/test_file_trash_client.py b/tests/app/services/file_manager/file_trash/test_file_trash_client.py index 125f04fc..fc66598b 100644 --- a/tests/app/services/file_manager/file_trash/test_file_trash_client.py +++ b/tests/app/services/file_manager/file_trash/test_file_trash_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -7,8 +7,7 @@ from app.configs.app_config import AppConfig from app.models.item import ItemStatus from app.services.file_manager.file_trash.file_trash_client import FileTrashClient -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import customized_error_msg +from app.services.output_manager.error_handler import ECustomizedError, customized_error_msg from tests.conftest import decoded_token diff --git a/tests/app/services/file_manager/file_upload/test_file_upload.py b/tests/app/services/file_manager/file_upload/test_file_upload.py index 5f5f800e..14830812 100644 --- a/tests/app/services/file_manager/file_upload/test_file_upload.py +++ b/tests/app/services/file_manager/file_upload/test_file_upload.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -8,14 +8,14 @@ from app.configs.app_config import AppConfig from app.models.item import ItemType -from app.services.file_manager.file_upload.file_upload import assemble_path -from app.services.file_manager.file_upload.file_upload import compress_folder_to_zip -from app.services.file_manager.file_upload.file_upload import resume_upload -from app.services.file_manager.file_upload.file_upload import simple_upload -from app.services.file_manager.file_upload.models import FileObject -from app.services.file_manager.file_upload.models import ItemStatus -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import customized_error_msg +from app.services.file_manager.file_upload.file_upload import ( + assemble_path, + compress_folder_to_zip, + resume_upload, + simple_upload, +) +from app.services.file_manager.file_upload.models import FileObject, ItemStatus +from app.services.output_manager.error_handler import ECustomizedError, customized_error_msg from tests.conftest import decoded_token diff --git a/tests/app/services/file_manager/file_upload/test_model.py b/tests/app/services/file_manager/file_upload/test_model.py index 48ad67e2..6d333915 100644 --- a/tests/app/services/file_manager/file_upload/test_model.py +++ b/tests/app/services/file_manager/file_upload/test_model.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/file_manager/file_upload/test_upload_client.py b/tests/app/services/file_manager/file_upload/test_upload_client.py index 37c18c64..54b8c7f4 100644 --- a/tests/app/services/file_manager/file_upload/test_upload_client.py +++ b/tests/app/services/file_manager/file_upload/test_upload_client.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/file_manager/test_manifest.py b/tests/app/services/file_manager/test_manifest.py index 86f9e42a..2509f57d 100644 --- a/tests/app/services/file_manager/test_manifest.py +++ b/tests/app/services/file_manager/test_manifest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2025 Indoc Systems +# Copyright (C) 2025-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/project_manager/test_project.py b/tests/app/services/project_manager/test_project.py index 08c11fd2..e6a551ff 100644 --- a/tests/app/services/project_manager/test_project.py +++ b/tests/app/services/project_manager/test_project.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/user_authentication/test_token_manager.py b/tests/app/services/user_authentication/test_token_manager.py index 3ca14d30..4f4ec355 100644 --- a/tests/app/services/user_authentication/test_token_manager.py +++ b/tests/app/services/user_authentication/test_token_manager.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023-2025 Indoc Systems +# Copyright (C) 2023-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/services/user_authentication/test_user_login_logout.py b/tests/app/services/user_authentication/test_user_login_logout.py index c9d3e799..7a532198 100644 --- a/tests/app/services/user_authentication/test_user_login_logout.py +++ b/tests/app/services/user_authentication/test_user_login_logout.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -6,9 +6,11 @@ from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig -from app.services.user_authentication.user_login_logout import check_is_login -from app.services.user_authentication.user_login_logout import user_device_id_login -from app.services.user_authentication.user_login_logout import validate_user_device_login +from app.services.user_authentication.user_login_logout import ( + check_is_login, + user_device_id_login, + validate_user_device_login, +) def test_check_is_not_login(mocker): diff --git a/tests/app/utils/test_aggregated.py b/tests/app/utils/test_aggregated.py index 5914ce0f..7156cb1c 100644 --- a/tests/app/utils/test_aggregated.py +++ b/tests/app/utils/test_aggregated.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -6,14 +6,16 @@ from app.configs.app_config import AppConfig from app.models.item import ItemType -from app.utils.aggregated import check_item_duplication -from app.utils.aggregated import get_latest_cli_version -from app.utils.aggregated import get_version_compatibility -from app.utils.aggregated import identify_target_folder -from app.utils.aggregated import normalize_input_paths -from app.utils.aggregated import normalize_join -from app.utils.aggregated import search_item -from app.utils.aggregated import validate_folder_name +from app.utils.aggregated import ( + check_item_duplication, + get_latest_cli_version, + get_version_compatibility, + identify_target_folder, + normalize_input_paths, + normalize_join, + search_item, + validate_folder_name, +) from tests.conftest import decoded_token test_project_code = 'testproject' diff --git a/tests/conftest.py b/tests/conftest.py index d39b4f62..846c42b6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2025 Indoc Systems +# Copyright (C) 2022-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. @@ -7,8 +7,7 @@ import pytest from app.configs.app_config import AppConfig -from app.configs.config import Settings -from app.configs.config import get_settings +from app.configs.config import Settings, get_settings from app.configs.user_config import UserConfig from app.models.singleton import Singleton diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index 0886395d..c7720cf6 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2023-2025 Indoc Systems +# Copyright (C) 2023-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/fixtures/fake.py b/tests/fixtures/fake.py index a252aaba..aad24a64 100644 --- a/tests/fixtures/fake.py +++ b/tests/fixtures/fake.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023-2025 Indoc Systems +# Copyright (C) 2023-2026 Indoc Systems # # Contact Indoc Systems for any questions regarding the use of this source code. From 9377a34b114d50fec5bd1d6e95b308ec448e2ecf Mon Sep 17 00:00:00 2001 From: zhiren Date: Tue, 20 Jan 2026 16:40:00 -0500 Subject: [PATCH 4/8] fixup the preupload timeout fails the resumable upload --- .../file_manager/file_upload/upload_client.py | 2 +- pyproject.toml | 2 +- tests/app/commands/test_user.py | 2 +- .../file_upload/test_file_upload.py | 22 ++++++++++--- .../file_upload/test_upload_client.py | 31 ++++++++++++++----- 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/app/services/file_manager/file_upload/upload_client.py b/app/services/file_manager/file_upload/upload_client.py index b12673a6..e225b375 100644 --- a/app/services/file_manager/file_upload/upload_client.py +++ b/app/services/file_manager/file_upload/upload_client.py @@ -163,7 +163,7 @@ def check_upload_duplication( return: - non_exist_file_objects(List[FileObject]): the file that need to be uploaded. - exist_files(List[str]): the file that has been uploaded. will be skipped - - [updated] reigstered_file_objects(List[Dict[str, Any]]): this is to handle the conner case + - [updated] registered_file_objects(List[Dict[str, Any]]): this is to handle the conner case where upload interrupted at specific batch. The local json manifest mismatches with backend metadata. So we need to return the registered file objects as well if possible. """ diff --git a/pyproject.toml b/pyproject.toml index 9cae2622..3effe3f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "app" -version = "3.20.1" +version = "3.21.0" description = "This service is designed to support pilot platform" authors = ["Indoc Systems"] diff --git a/tests/app/commands/test_user.py b/tests/app/commands/test_user.py index 4f55ef4c..09d4cd3f 100644 --- a/tests/app/commands/test_user.py +++ b/tests/app/commands/test_user.py @@ -108,7 +108,7 @@ def test_login_command_with_newer_version_available_message( result = cli_runner.invoke(login) assert result.exit_code == 0 - assert login_using_api_key_mock.called_once_with(api_key) + login_using_api_key_mock.assert_called_once_with(api_key) if Version(current_version) < Version(new_version): except_message = mhandler.SrvOutPutHandler.newer_version_available( new_version, download_url, print_message=False diff --git a/tests/app/services/file_manager/file_upload/test_file_upload.py b/tests/app/services/file_manager/file_upload/test_file_upload.py index 14830812..c6f1bbbd 100644 --- a/tests/app/services/file_manager/file_upload/test_file_upload.py +++ b/tests/app/services/file_manager/file_upload/test_file_upload.py @@ -276,7 +276,7 @@ def test_folder_merge_succuss_with_no_duplication(mocker, mock_upload_client): non_dup_list = [FileObject('object/path', 'local_path', 'resumable_id', 'job_id', 'item_id')] mocker.patch( 'app.services.file_manager.file_upload.file_upload.UploadClient.check_upload_duplication', - return_value=(non_dup_list, []), + return_value=(non_dup_list, [], []), ) item_ids = simple_upload(upload_event) @@ -301,7 +301,7 @@ def test_folder_merge_succuss_with_duplication(mocker, mock_upload_client): dup_list = ['object/dup'] mocker.patch( 'app.services.file_manager.file_upload.file_upload.UploadClient.check_upload_duplication', - return_value=(non_dup_list, dup_list), + return_value=(non_dup_list, dup_list, []), ) item_ids = simple_upload(upload_event) @@ -326,7 +326,7 @@ def test_folder_merge_skip_with_all_duplication(mocker, mock_upload_client, capf dup_list = ['object/dup'] mocker.patch( 'app.services.file_manager.file_upload.file_upload.UploadClient.check_upload_duplication', - return_value=([], dup_list), + return_value=([], dup_list, []), ) try: @@ -368,7 +368,7 @@ def test_upload_folder_as_zip(mocker, mock_upload_client): non_dup_list = [FileObject('object/path', 'local_path', 'resumable_id', 'job_id', 'item_id')] mocker.patch( 'app.services.file_manager.file_upload.file_upload.UploadClient.check_upload_duplication', - return_value=(non_dup_list, []), + return_value=(non_dup_list, [], []), ) item_ids = simple_upload(upload_event) assert len(item_ids) == 1 @@ -391,6 +391,7 @@ def test_resume_upload(mocker): 'registered_items': {test_obj.item_id: test_obj.to_dict()}, 'unregistered_items': {}, 'total_size': 1, + 'resumable_manifest_file': 'resumable_manifest_file', } get_return = test_obj.to_dict() @@ -407,11 +408,15 @@ def test_resume_upload(mocker): 'os.path.getsize', return_value=1, ) + output_manifest_mock = mocker.patch( + 'app.services.file_manager.file_upload.file_upload.UploadClient.output_manifest', return_value={} + ) resume_upload(manifest_json, 1) get_mock.assert_called_once() resume_upload_mock.assert_called_once() + output_manifest_mock.assert_called_once() def test_resume_upload_failed_when_REGISTERED_doesnt_exist(mocker, capfd): @@ -427,6 +432,7 @@ def test_resume_upload_failed_when_REGISTERED_doesnt_exist(mocker, capfd): 'tags': 'tags', 'registered_items': {test_obj.item_id: test_obj.to_dict()}, 'unregistered_items': {}, + 'resumable_manifest_file': 'resumable_manifest_file', } get_return = test_obj.to_dict() @@ -437,6 +443,9 @@ def test_resume_upload_failed_when_REGISTERED_doesnt_exist(mocker, capfd): resume_upload_mock = mocker.patch( 'app.services.file_manager.file_upload.file_upload.UploadClient.resume_upload', return_value=[] ) + resumable_manifest_mock = mocker.patch( + 'app.services.file_manager.file_upload.file_upload.UploadClient.output_manifest', return_value={} + ) try: resume_upload(manifest_json, 1) @@ -447,6 +456,7 @@ def test_resume_upload_failed_when_REGISTERED_doesnt_exist(mocker, capfd): get_mock.assert_called_once() assert resume_upload_mock.call_count == 0 + assert resumable_manifest_mock.call_count == 0 def test_resume_upload_integrity_check_failed(mocker, capfd): @@ -484,6 +494,9 @@ def test_resume_upload_integrity_check_failed(mocker, capfd): 'os.path.getsize', return_value=2, ) + resumable_manifest_mock = mocker.patch( + 'app.services.file_manager.file_upload.file_upload.UploadClient.output_manifest', return_value={} + ) resume_upload(manifest_json, 1) out, _ = capfd.readouterr() @@ -492,3 +505,4 @@ def test_resume_upload_integrity_check_failed(mocker, capfd): get_mock.assert_called_once() resume_upload_mock.assert_called_once() + resumable_manifest_mock.assert_called_once() diff --git a/tests/app/services/file_manager/file_upload/test_upload_client.py b/tests/app/services/file_manager/file_upload/test_upload_client.py index 54b8c7f4..2a62ac3b 100644 --- a/tests/app/services/file_manager/file_upload/test_upload_client.py +++ b/tests/app/services/file_manager/file_upload/test_upload_client.py @@ -313,17 +313,32 @@ def test_check_upload_duplication_success(httpx_mock, mocker, case_insensitive): mocker.patch('app.services.file_manager.file_upload.models.FileObject.generate_meta', return_value=(1, 1)) dup_obj = FileObject('object/duplicate', 'local_path', 'resumable_id', 'job_id', 'item_id') not_dup_object = FileObject('object/not_duplicate', 'local_path', 'resumable_id', 'job_id', 'item_id') + registered_item = FileObject('object/registered', 'local_path', 'resumable_id', 'job_id', 'item_id') - url = AppConfig.Connections.url_base + '/portal/v1/files/exists' + url = AppConfig.Connections.url_bff + '/v2/items/batch/exists' httpx_mock.add_response( method='POST', url=url, - json={'result': [dup_obj.object_path.upper() if case_insensitive else dup_obj.object_path]}, + json={ + 'result': [ + {'parent_path': 'object', 'name': 'duplicate', 'status': ItemStatus.ACTIVE}, + {'parent_path': 'object', 'name': 'registered', 'status': ItemStatus.REGISTERED}, + ] + }, ) - not_dup_list, dup_list = upload_client.check_upload_duplication([dup_obj, not_dup_object]) - assert not_dup_list == [not_dup_object] - assert dup_list == [dup_obj.object_path.upper() if case_insensitive else dup_obj.object_path] + not_dup_list, dup_list, registered_list = upload_client.check_upload_duplication([dup_obj, not_dup_object]) + + assert len(not_dup_list) == 1 + assert not_dup_list[0] == not_dup_object + + assert len(dup_list) == 1 + assert dup_list[0] == dup_obj.object_path + + assert len(registered_list) == 1 + assert registered_item.object_path == registered_list[0].get('parent_path', '') + '/' + registered_list[0].get( + 'name', '' + ) def test_check_upload_duplication_fail_with_403(httpx_mock, mocker, capfd): @@ -333,7 +348,7 @@ def test_check_upload_duplication_fail_with_403(httpx_mock, mocker, capfd): ) upload_client = UploadClient('project_code', 'parent_folder_id') - url = AppConfig.Connections.url_base + '/portal/v1/files/exists' + url = AppConfig.Connections.url_bff + '/v2/items/batch/exists' httpx_mock.add_response( method='POST', url=url, @@ -360,7 +375,7 @@ def test_check_upload_duplication_fail_with_500(httpx_mock, mocker, capfd): ) upload_client = UploadClient('project_code', 'parent_folder_id') - url = AppConfig.Connections.url_base + '/portal/v1/files/exists' + url = AppConfig.Connections.url_bff + '/v2/items/batch/exists' httpx_mock.add_response( method='POST', url=url, @@ -403,7 +418,7 @@ def test_output_manifest_success_for_resumable_upload(mocker, tmp_path): assert file_item.get('item_id') == 'item_id_1' assert len(res.get('unregistered_items')) == 1 - file_item = res.get('unregistered_items').get('local_path_2') # unregistered item dont have id + file_item = res.get('unregistered_items').get('object/test_obj_unregistered') # unregistered item dont have id assert file_item.get('resumable_id') == 'resumable_id_2' assert file_item.get('local_path') == 'local_path_2' assert file_item.get('object_path') == 'object/test_obj_unregistered' From 07f72af7f89b1015cddaf9d553383c84e16d3bdf Mon Sep 17 00:00:00 2001 From: zhiren Date: Tue, 20 Jan 2026 16:57:26 -0500 Subject: [PATCH 5/8] fixup the resumable upload didn't output correct batches --- .../file_manager/file_upload/file_upload.py | 6 +- .../file_upload/test_file_upload.py | 191 ++++++++++++++++++ 2 files changed, 194 insertions(+), 3 deletions(-) diff --git a/app/services/file_manager/file_upload/file_upload.py b/app/services/file_manager/file_upload/file_upload.py index 3e554fa4..708da93f 100644 --- a/app/services/file_manager/file_upload/file_upload.py +++ b/app/services/file_manager/file_upload/file_upload.py @@ -439,11 +439,11 @@ def resume_upload( logger.info(f'Unregistered items: {len(unregistered_items)}') # redo preupload again - batch_count = 1 + processed_count = 0 for file_batchs in batch_generator(unregistered_items, batch_size=AppConfig.Env.upload_batch_size): unfinished_items.extend(upload_client.pre_upload(file_batchs)) - upload_client.output_manifest(unfinished_items, unregistered_items[batch_count + 1 :], resumable_manifest_file) - batch_count += 1 + processed_count += len(file_batchs) + upload_client.output_manifest(unfinished_items, unregistered_items[processed_count:], resumable_manifest_file) mhandler.SrvOutPutHandler.resume_warning(len(unfinished_items)) mhandler.SrvOutPutHandler.resume_check_success() diff --git a/tests/app/services/file_manager/file_upload/test_file_upload.py b/tests/app/services/file_manager/file_upload/test_file_upload.py index c6f1bbbd..a3d454c3 100644 --- a/tests/app/services/file_manager/file_upload/test_file_upload.py +++ b/tests/app/services/file_manager/file_upload/test_file_upload.py @@ -3,6 +3,7 @@ # Contact Indoc Systems for any questions regarding the use of this source code. import os +from unittest.mock import MagicMock import click @@ -506,3 +507,193 @@ def test_resume_upload_integrity_check_failed(mocker, capfd): get_mock.assert_called_once() resume_upload_mock.assert_called_once() resumable_manifest_mock.assert_called_once() + + +def test_resume_upload_manifest_updates_correctly_after_each_batch(mocker): + """ + Test that the manifest file is updated correctly after each batch, + ensuring the remaining unregistered_items slice is accurate. + """ + # Mock dependencies + mocker.patch( + 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', + return_value=decoded_token(), + ) + mocker.patch('app.services.file_manager.file_upload.models.FileObject.generate_meta', return_value=(1, 1)) + + # Create test file objects + test_files = [] + for i in range(7): # 7 files, with batch size of 3, should be 3 batches: [3, 3, 1] + test_obj = FileObject(f'object/path_{i}', f'local_path_{i}', f'resumable_id_{i}', f'job_id_{i}', f'item_id_{i}') + test_obj.total_size = 1 + test_files.append(test_obj) + + manifest_json = { + 'project_code': 'project_code', + 'operator': 'operator', + 'zone': AppConfig.Env.green_zone, + 'parent_folder_id': 'parent_folder_id', + 'current_folder_node': 'current_folder_node', + 'tags': 'tags', + 'registered_items': {}, + 'unregistered_items': {f'object/path_{i}': test_files[i].to_dict() for i in range(7)}, + 'total_size': 7, + 'resumable_manifest_file': 'test_manifest.json', + } + + # Mock the upload client and its methods + mock_upload_client = MagicMock() + mock_upload_client.pre_upload.return_value = [] # Return empty list for simplicity + + # Track calls to output_manifest to verify correct remaining items + manifest_calls = [] + + def track_manifest_calls(finished_items, remaining_items, manifest_file): + manifest_calls.append( + { + 'finished_count': len(finished_items), + 'remaining_count': len(remaining_items), + 'remaining_items': [ + item.object_path if hasattr(item, 'object_path') else str(item) for item in remaining_items + ], + } + ) + + mock_upload_client.output_manifest.side_effect = track_manifest_calls + + # Mock the UploadClient constructor + mocker.patch('app.services.file_manager.file_upload.file_upload.UploadClient', return_value=mock_upload_client) + + # Mock other dependencies + mocker.patch('app.services.file_manager.file_upload.file_upload.resume_get_unfinished_items', return_value=[]) + mocker.patch( + 'app.services.file_manager.file_upload.file_upload.item_duplication_check', return_value=(test_files, []) + ) # Return all files as unregistered + mocker.patch( + 'app.services.file_manager.file_upload.file_upload.batch_generator', + side_effect=lambda items, batch_size: [items[i : i + batch_size] for i in range(0, len(items), batch_size)], + ) + + # Set batch size to 3 for testing + mocker.patch.object(AppConfig.Env, 'upload_batch_size', 3) + + # Mock threading components + mocker.patch('app.services.file_manager.file_upload.file_upload.ThreadPool') + mocker.patch('app.services.output_manager.message_handler.SrvOutPutHandler') + + # Execute the function + resume_upload(manifest_json, 1) + + # Verify output_manifest was called correctly for each batch + # Should be called 4 times: 1 initial + 3 batch calls + expected_calls = 4 + assert len(manifest_calls) == expected_calls, f"Expected {expected_calls} manifest calls, got {len(manifest_calls)}" + + # Verify the remaining items count decreases correctly after each batch + batch_calls = manifest_calls[1:] # Skip the initial call, focus on batch processing + + # First batch: processed 3, remaining 4 + assert ( + batch_calls[0]['remaining_count'] == 4 + ), f"After batch 1, expected 4 remaining, got {batch_calls[0]['remaining_count']}" + + # Second batch: processed 6, remaining 1 + assert ( + batch_calls[1]['remaining_count'] == 1 + ), f"After batch 2, expected 1 remaining, got {batch_calls[1]['remaining_count']}" + + # Third batch: processed 7, remaining 0 + assert ( + batch_calls[2]['remaining_count'] == 0 + ), f"After batch 3, expected 0 remaining, got {batch_calls[2]['remaining_count']}" + + # Verify the actual remaining items are correct (not just the count) + # After first batch, should have items 3,4,5,6 remaining + expected_remaining_after_batch1 = ['object/path_3', 'object/path_4', 'object/path_5', 'object/path_6'] + assert batch_calls[0]['remaining_items'] == expected_remaining_after_batch1 + + # After second batch, should have item 6 remaining + expected_remaining_after_batch2 = ['object/path_6'] + assert batch_calls[1]['remaining_items'] == expected_remaining_after_batch2 + + # After third batch, should have no items remaining + assert batch_calls[2]['remaining_items'] == [] + + +def test_resume_upload_manifest_handles_uneven_batches(mocker): + """ + Test that manifest updates work correctly with uneven batch sizes + (e.g., when the last batch has fewer items than batch_size) + """ + # Mock dependencies + mocker.patch( + 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', + return_value=decoded_token(), + ) + mocker.patch('app.services.file_manager.file_upload.models.FileObject.generate_meta', return_value=(1, 1)) + + # Create 5 test files with batch size of 3 -> batches: [3, 2] + test_files = [] + for i in range(5): + test_obj = FileObject(f'object/path_{i}', f'local_path_{i}', f'resumable_id_{i}', f'job_id_{i}', f'item_id_{i}') + test_obj.total_size = 1 + test_files.append(test_obj) + + manifest_json = { + 'project_code': 'project_code', + 'operator': 'operator', + 'zone': AppConfig.Env.green_zone, + 'parent_folder_id': 'parent_folder_id', + 'current_folder_node': 'current_folder_node', + 'tags': 'tags', + 'registered_items': {}, + 'unregistered_items': {f'object/path_{i}': test_files[i].to_dict() for i in range(5)}, + 'total_size': 5, + 'resumable_manifest_file': 'test_manifest.json', + } + + # Mock the upload client + mock_upload_client = MagicMock() + mock_upload_client.pre_upload.return_value = [] + + # Track manifest calls + manifest_calls = [] + + def track_manifest_calls(finished_items, remaining_items, manifest_file): + manifest_calls.append( + { + 'remaining_count': len(remaining_items), + } + ) + + mock_upload_client.output_manifest.side_effect = track_manifest_calls + + # Mock dependencies + mocker.patch('app.services.file_manager.file_upload.file_upload.UploadClient', return_value=mock_upload_client) + mocker.patch('app.services.file_manager.file_upload.file_upload.resume_get_unfinished_items', return_value=[]) + mocker.patch( + 'app.services.file_manager.file_upload.file_upload.item_duplication_check', return_value=(test_files, []) + ) + mocker.patch( + 'app.services.file_manager.file_upload.file_upload.batch_generator', + side_effect=lambda items, batch_size: [items[i : i + batch_size] for i in range(0, len(items), batch_size)], + ) + + # Set batch size to 3 + mocker.patch.object(AppConfig.Env, 'upload_batch_size', 3) + + # Mock threading components + mocker.patch('app.services.file_manager.file_upload.file_upload.ThreadPool') + mocker.patch('app.services.output_manager.message_handler.SrvOutPutHandler') + + # Execute the function + resume_upload(manifest_json, 1) + + # Should have 3 calls: 1 initial + 2 batch calls + batch_calls = manifest_calls[1:] # Skip initial call + + # After first batch (3 items processed): remaining = 2 + assert batch_calls[0]['remaining_count'] == 2 + + # After second batch (5 items processed): remaining = 0 + assert batch_calls[1]['remaining_count'] == 0 From 065e6a33fd43e769d74020506a40edff595eedfb Mon Sep 17 00:00:00 2001 From: zhiren Date: Wed, 21 Jan 2026 15:40:11 -0500 Subject: [PATCH 6/8] update version matrix pipeline --- .github/workflows/update_compatible_version.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/update_compatible_version.yml b/.github/workflows/update_compatible_version.yml index 2f85d6e0..479f48cb 100644 --- a/.github/workflows/update_compatible_version.yml +++ b/.github/workflows/update_compatible_version.yml @@ -39,33 +39,30 @@ jobs: - name: Extract Versions from PR if: ${{ steps.check_version.outputs.current != steps.check_version.outputs.last }} id: extract_versions + shell: bash run: | - - PR_BODY="${{ github.event.pull_request.body }}" + # Dump PR body to a file WITHOUT bash interpreting backticks / $(...) + cat > pr_body.md <<'__PR_BODY__' + ${{ github.event.pull_request.body }} + __PR_BODY__ # Extract the "## Versions" section - echo "$PR_BODY" | awk '/## Versions/{flag=1; next} /^## /{flag=0} flag' > PR_VERSIONS.md + awk '/## Versions/{flag=1; next} /^## /{flag=0} flag' pr_body.md > PR_VERSIONS.md - # Read extracted content CLI_VERSION=$(grep -oP '(?<=- Pilot Release Version: ).*' PR_VERSIONS.md | tr -d '\r') COMPATIBLE_VERSION=$(grep -oP '(?<=- Compatible Version: ).*' PR_VERSIONS.md | tr -d '\r') - # Handle missing values CLI_VERSION=${CLI_VERSION:-"Unknown"} COMPATIBLE_VERSION=${COMPATIBLE_VERSION:-"Unknown"} - # Get the latest Git tag (assuming it's the CLI version) LATEST_CLI_VERSION=$(grep '^version =' pyproject.toml | sed -E 's/version = "(.*)"/\1/') - # Convert to NDJSON format NEW_ENTRY="{\"Cli Version\": \"$LATEST_CLI_VERSION\", \"Pilot Release Version\": \"$CLI_VERSION\", \"Compatible Version\": \"$COMPATIBLE_VERSION\"}" - # Ensure file exists before appending touch docs/compatible_version.ndjson - - # Append new entry to NDJSON file echo "$NEW_ENTRY" >> docs/compatible_version.ndjson + - name: Embed Compatible Versions into README if: ${{ steps.check_version.outputs.current != steps.check_version.outputs.last }} run: | From 86e8f520eb973f61d0907e9926b78acf4b33b376 Mon Sep 17 00:00:00 2001 From: zhiren Date: Wed, 21 Jan 2026 15:52:19 -0500 Subject: [PATCH 7/8] update version matrix pipeline --- .github/workflows/update_compatible_version.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/update_compatible_version.yml b/.github/workflows/update_compatible_version.yml index 479f48cb..49b341ee 100644 --- a/.github/workflows/update_compatible_version.yml +++ b/.github/workflows/update_compatible_version.yml @@ -49,15 +49,23 @@ jobs: # Extract the "## Versions" section awk '/## Versions/{flag=1; next} /^## /{flag=0} flag' pr_body.md > PR_VERSIONS.md - CLI_VERSION=$(grep -oP '(?<=- Pilot Release Version: ).*' PR_VERSIONS.md | tr -d '\r') - COMPATIBLE_VERSION=$(grep -oP '(?<=- Compatible Version: ).*' PR_VERSIONS.md | tr -d '\r') + CLI_VERSION="$(grep -oP '(?<=- Pilot Release Version: ).*' PR_VERSIONS.md 2>/dev/null || true)" + CLI_VERSION="${CLI_VERSION//$'\r'/}" + + COMPATIBLE_VERSION="$(grep -oP '(?<=- Compatible Version: ).*' PR_VERSIONS.md 2>/dev/null || true)" + COMPATIBLE_VERSION="${COMPATIBLE_VERSION//$'\r'/}" CLI_VERSION=${CLI_VERSION:-"Unknown"} COMPATIBLE_VERSION=${COMPATIBLE_VERSION:-"Unknown"} - LATEST_CLI_VERSION=$(grep '^version =' pyproject.toml | sed -E 's/version = "(.*)"/\1/') + LAST_VERSION="$( + tail -n 1 docs/compatible_version.ndjson 2>/dev/null \ + | jq -r '."Cli Version"' 2>/dev/null \ + || true + )" + LAST_VERSION=${LAST_VERSION:-""} - NEW_ENTRY="{\"Cli Version\": \"$LATEST_CLI_VERSION\", \"Pilot Release Version\": \"$CLI_VERSION\", \"Compatible Version\": \"$COMPATIBLE_VERSION\"}" + NEW_ENTRY="{\"Cli Version\": \"$LAST_VERSION\", \"Pilot Release Version\": \"$CLI_VERSION\", \"Compatible Version\": \"$COMPATIBLE_VERSION\"}" touch docs/compatible_version.ndjson echo "$NEW_ENTRY" >> docs/compatible_version.ndjson From a9bddf38ffc3f81e58940146c33078738bc6b00b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 21 Jan 2026 20:52:32 +0000 Subject: [PATCH 8/8] Updated docs/compatible_version.ndjson with PR #249 --- README.md | 3 ++- docs/compatible_version.ndjson | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8544334b..9b3491f3 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,8 @@ Command line tool that allows the user to execute data operations on the platfor | 3.19.6 | 2.15.2 | 2.15.2 | | 3.19.7 | 2.15.2 | 2.15.2 | | 3.20.0 | 2.16.0 | 2.15.2 | -| 3.20.1 | 2.16 | 2.16 | +| 3.20.1 | 2.16.0 | 2.16.0 | +| 3.20.1 | Unknown | Unknown | ## Build Instructions diff --git a/docs/compatible_version.ndjson b/docs/compatible_version.ndjson index 7bddc8da..55e1bb82 100644 --- a/docs/compatible_version.ndjson +++ b/docs/compatible_version.ndjson @@ -16,3 +16,4 @@ {"Cli Version": "3.19.7", "Pilot Release Version": "2.15.2", "Compatible Version": "2.15.2"} {"Cli Version": "3.20.0", "Pilot Release Version": "2.16.0", "Compatible Version": "2.15.2"} {"Cli Version": "3.20.1", "Pilot Release Version": "2.16.0", "Compatible Version": "2.16.0"} +{"Cli Version": "3.20.1", "Pilot Release Version": "Unknown", "Compatible Version": "Unknown"}