From cf56e3ca3e853d89bdaa9f57b4e0ce89c4a824c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=88=9A=28noham=29=C2=B2?= <100566912+NohamR@users.noreply.github.com> Date: Fri, 12 Dec 2025 18:26:16 +0100 Subject: [PATCH 1/5] Update docs, add dev dependencies, and test script Revise README with improved installation and usage instructions, add development section, and simplify Python usage example. Bump version to 1.1.2, add optional 'dev' dependencies (pylint) in pyproject.toml, and introduce a minimal test.py script for upload testing. --- README.md | 83 +++--- pyproject.toml | 7 +- test.py | 7 + uv.lock | 702 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 733 insertions(+), 66 deletions(-) create mode 100644 test.py diff --git a/README.md b/README.md index 4eb8f2e..0c884f1 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,24 @@ It supports the free API tiers, streaming uploads (low memory usage for large fi ### From Source -1. Clone the repository. -2. Install via pip: - +1. Clone the repository: ```bash -pip install . +git clone https://github.com/garnajee/gofilepy.git && cd gofilepy +``` + +2. Install via [uv](https://docs.astral.sh/uv/getting-started/installation/): +```bash +uv sync +``` + +3. Running the CLI +```bash +uv run gofilepy --help +``` + +4. (Optional) Install the package in your environment +```bash +uv pip install . ``` ## Usage (CLI) @@ -75,56 +88,32 @@ gofilepy -vv big_file.iso You can use `gofilepy` in your own Python scripts. ```python -import os from gofilepy import GofileClient -from tqdm import tqdm -TOKEN = os.environ.get("GOFILE_TOKEN") # in .env file, or put it here -FILES_TO_UPLOAD = [ - "/path/to/video1.mp4", - "/path/to/image.jpg" -] -FOLDER_ID = None # None to create new folder, or put folder ID here +client = GofileClient() +# client = GofileClient(token="YOUR_TOKEN_HERE") # Optional token for private uploads +file = client.upload(file=open("./test.py", "rb")) +print(file.name) +print(file.page_link) # View and download file at this link +``` -def upload_files(): - client = GofileClient(token=TOKEN) - - print(f"Starting upload... {len(FILES_TO_UPLOAD)}") +## Development - for file_path in FILES_TO_UPLOAD: - if not os.path.exists(file_path): - print(f"❌ File not found: {file_path}") - continue +For contributors and developers: - filename = os.path.basename(file_path) - filesize = os.path.getsize(file_path) +1. Install with development dependencies: +```bash +uv sync --extra dev +``` - with tqdm(total=filesize, unit='B', unit_scale=True, desc=filename) as pbar: - - def progress_callback(bytes_read): - uploaded_so_far = pbar.n - pbar.update(bytes_read - uploaded_so_far) +2. Run pylint to check code quality: +```bash +uv run pylint src/gofilepy +``` - try: - response = client.upload_file( - file_path=file_path, - folder_id=FOLDER_ID, - callback=progress_callback - ) - - global FOLDER_ID - if FOLDER_ID is None and 'parentFolder' in response: - FOLDER_ID = response['parentFolder'] - - pbar.update(filesize - pbar.n) - - tqdm.write(f"✅ Success : {response.get('downloadPage')}") - - except Exception as e: - tqdm.write(f"❌ Error, {filename}: {e}") - -if __name__ == "__main__": - upload_files() +3. Install in editable mode for development: +```bash +uv pip install -e . ``` ## Building for Release diff --git a/pyproject.toml b/pyproject.toml index cea94eb..5637b36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "gofilepy" -version = "1.1.1" +version = "1.1.2" description = "A Python CLI and Library for gofile.io" readme = "README.md" authors = [{ name = "Garnajee", email = "62147746+garnajee@users.noreply.github.com" }] @@ -21,5 +21,10 @@ dependencies = [ "tqdm", ] +[project.optional-dependencies] +dev = [ + "pylint", +] + [project.scripts] gofilepy = "gofilepy.cli:main" diff --git a/test.py b/test.py new file mode 100644 index 0000000..be4f14e --- /dev/null +++ b/test.py @@ -0,0 +1,7 @@ +from gofilepy import GofileClient + +client = GofileClient() +# client = GofileClient(token="YOUR_TOKEN_HERE") # Optional token for private uploads +file = client.upload(file=open("./test.py", "rb")) +print(file.name) +print(file.page_link) # View and download file at this link \ No newline at end of file diff --git a/uv.lock b/uv.lock index 937096a..4dc68b4 100644 --- a/uv.lock +++ b/uv.lock @@ -2,9 +2,13 @@ version = 1 revision = 3 requires-python = ">=3.7" resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", "python_full_version == '3.8.*'", - "python_full_version < '3.8'", + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", ] [[package]] @@ -12,7 +16,8 @@ name = "anyio" version = "3.7.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.8'", + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", ] dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.8'" }, @@ -48,7 +53,10 @@ name = "anyio" version = "4.12.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", ] dependencies = [ { name = "exceptiongroup", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, @@ -60,6 +68,90 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" }, ] +[[package]] +name = "astroid" +version = "2.11.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.7.2'", +] +dependencies = [ + { name = "lazy-object-proxy", marker = "python_full_version < '3.7.2'" }, + { name = "setuptools", marker = "python_full_version < '3.7.2'" }, + { name = "typed-ast", marker = "python_full_version < '3.7.2' and implementation_name == 'cpython'" }, + { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7.2'" }, + { name = "wrapt", marker = "python_full_version < '3.7.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/fa/cedd4cf37634b2fcc3773cedd0a9ca05fed2fa014d3d03815b04b7738ade/astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946", size = 206015, upload-time = "2022-07-09T14:51:00.971Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/3b/f1aa1bd41e8188b3a3605d71b699b73695fc7ac862cbed23ed9dee707251/astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b", size = 251173, upload-time = "2022-07-09T14:50:59.226Z" }, +] + +[[package]] +name = "astroid" +version = "2.15.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.7.2' and python_full_version < '3.8'", +] +dependencies = [ + { name = "lazy-object-proxy", marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "typed-ast", marker = "python_full_version >= '3.7.2' and python_full_version < '3.8' and implementation_name == 'cpython'" }, + { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "wrapt", marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/3d/c18b0854d0d2eb3aca20c149cff5c90e6b84a5366066768d98636f5045ed/astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a", size = 344362, upload-time = "2023-09-26T12:40:28.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/b6/c0b5394ec6149e0129421f1a762b805e0e583974bc3cd65e3c7ce7c95444/astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c", size = 278329, upload-time = "2023-09-26T12:40:25.988Z" }, +] + +[[package]] +name = "astroid" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.8.*'", +] +dependencies = [ + { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/53/1067e1113ecaf58312357f2cd93063674924119d80d173adc3f6f2387aa2/astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", size = 397576, upload-time = "2024-07-20T12:57:43.26Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/96/b32bbbb46170a1c8b8b1f28c794202e25cfe743565e9d3469b8eb1e0cc05/astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25", size = 276348, upload-time = "2024-07-20T12:57:40.886Z" }, +] + +[[package]] +name = "astroid" +version = "3.3.11" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +dependencies = [ + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/74/dfb75f9ccd592bbedb175d4a32fc643cf569d7c218508bfbd6ea7ef9c091/astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce", size = 400439, upload-time = "2025-07-13T18:04:23.177Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/0f/3b8fdc946b4d9cc8cc1e8af42c4e409468c84441b933d037e101b3d72d86/astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec", size = 275612, upload-time = "2025-07-13T18:04:21.07Z" }, +] + +[[package]] +name = "astroid" +version = "4.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/22/97df040e15d964e592d3a180598ace67e91b7c559d8298bdb3c949dc6e42/astroid-4.0.2.tar.gz", hash = "sha256:ac8fb7ca1c08eb9afec91ccc23edbd8ac73bb22cbdd7da1d488d9fb8d6579070", size = 405714, upload-time = "2025-11-09T21:21:18.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/ac/a85b4bfb4cf53221513e27f33cc37ad158fce02ac291d18bee6b49ab477d/astroid-4.0.2-py3-none-any.whl", hash = "sha256:d7546c00a12efc32650b19a2bb66a153883185d3179ab0d4868086f807338b9b", size = 276354, upload-time = "2025-11-09T21:21:16.54Z" }, +] + [[package]] name = "certifi" version = "2025.11.12" @@ -78,6 +170,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "dill" +version = "0.3.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/31/54dd222e02311c2dbc9e680d37cbd50f4494ce1ee9b04c69980e4ec26f38/dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03", size = 183355, upload-time = "2023-07-22T22:18:46.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/3a/74a29b11cf2cdfcd6ba89c0cecd70b37cd1ba7b77978ce611eb7a146a832/dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", size = 115254, upload-time = "2023-07-22T22:18:44.511Z" }, +] + +[[package]] +name = "dill" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", + "python_full_version == '3.8.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, +] + [[package]] name = "exceptiongroup" version = "1.3.1" @@ -85,7 +206,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, - { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -94,7 +215,7 @@ wheels = [ [[package]] name = "gofilepy" -version = "1.1.0" +version = "1.1.2" source = { editable = "." } dependencies = [ { name = "httpx", version = "0.24.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, @@ -105,19 +226,31 @@ dependencies = [ { name = "tqdm" }, ] +[package.optional-dependencies] +dev = [ + { name = "pylint", version = "2.13.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7.2'" }, + { name = "pylint", version = "2.17.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "pylint", version = "3.2.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "pylint", version = "3.3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "pylint", version = "4.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + [package.metadata] requires-dist = [ { name = "httpx" }, + { name = "pylint", marker = "extra == 'dev'" }, { name = "python-dotenv", specifier = ">=0.21.1" }, { name = "tqdm" }, ] +provides-extras = ["dev"] [[package]] name = "h11" version = "0.14.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.8'", + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", ] dependencies = [ { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, @@ -132,7 +265,10 @@ name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", "python_full_version == '3.8.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } @@ -145,7 +281,8 @@ name = "httpcore" version = "0.17.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.8'", + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", ] dependencies = [ { name = "anyio", version = "3.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, @@ -163,7 +300,10 @@ name = "httpcore" version = "1.0.9" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", "python_full_version == '3.8.*'", ] dependencies = [ @@ -180,7 +320,8 @@ name = "httpx" version = "0.24.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.8'", + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", ] dependencies = [ { name = "certifi", marker = "python_full_version < '3.8'" }, @@ -198,7 +339,10 @@ name = "httpx" version = "0.28.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", "python_full_version == '3.8.*'", ] dependencies = [ @@ -218,7 +362,8 @@ name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.8'", + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", ] sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ @@ -230,7 +375,10 @@ name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", "python_full_version == '3.8.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } @@ -238,12 +386,300 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version == '3.9.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + +[[package]] +name = "isort" +version = "5.11.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/67/63/18cc5c2f9084d3f91ce704f2b5c8e17bedd777244e7732c21a31992b0a78/isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db", size = 187953, upload-time = "2023-01-30T09:45:12.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/f6/c55db45970fbd14de6ab72082f1b8a143c3a69aa031c1e0dd4b9ecc8d496/isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746", size = 104094, upload-time = "2023-01-30T09:45:09.902Z" }, +] + +[[package]] +name = "isort" +version = "5.13.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.8.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303, upload-time = "2023-12-13T20:37:26.124Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310, upload-time = "2023-12-13T20:37:23.244Z" }, +] + +[[package]] +name = "isort" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version == '3.9.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/82/fa43935523efdfcce6abbae9da7f372b627b27142c3419fcf13bf5b0c397/isort-6.1.0.tar.gz", hash = "sha256:9b8f96a14cfee0677e78e941ff62f03769a06d412aabb9e2a90487b3b7e8d481", size = 824325, upload-time = "2025-10-01T16:26:45.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/cc/9b681a170efab4868a032631dea1e8446d8ec718a7f657b94d49d1a12643/isort-6.1.0-py3-none-any.whl", hash = "sha256:58d8927ecce74e5087aef019f778d4081a3b6c98f15a80ba35782ca8a2097784", size = 94329, upload-time = "2025-10-01T16:26:43.291Z" }, +] + +[[package]] +name = "isort" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187", size = 805049, upload-time = "2025-10-11T13:30:59.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672, upload-time = "2025-10-11T13:30:57.665Z" }, +] + +[[package]] +name = "lazy-object-proxy" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c0/8bab72a73607d186edad50d0168ca85bd2743cfc55560c9d721a94654b20/lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae", size = 42830, upload-time = "2023-01-04T19:56:19.207Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/93/e921f7a795e252df7248e0d220dc69a9443ad507fe258dea51a32e5435ca/lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7", size = 22446, upload-time = "2023-01-04T19:55:58.968Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5d/25b9007c65f45805e711b56beac50ba395214e9e556cc8ee57f0882f88a9/lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4", size = 64145, upload-time = "2023-01-04T19:56:00.66Z" }, + { url = "https://files.pythonhosted.org/packages/27/a1/7cc10ca831679c5875c18ae6e0a468f7787ecd31fdd53598f91ea50df58d/lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd", size = 63277, upload-time = "2023-01-04T19:56:02.002Z" }, + { url = "https://files.pythonhosted.org/packages/31/ad/e8605300f51061284cc57ca0f4ef582047c7f309bda1bb1c3c19b64af5c9/lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701", size = 65902, upload-time = "2023-01-04T19:56:03.336Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f4/95999792ce5f9223bac10cb31b1724723ecd0ba13e081c5fb806d7f5b9c4/lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46", size = 65459, upload-time = "2023-01-04T19:56:04.511Z" }, + { url = "https://files.pythonhosted.org/packages/4d/7b/a959ff734bd3d8df7b761bfeaec6428549b77267072676a337b774f3b3ef/lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455", size = 20726, upload-time = "2023-01-04T19:56:06.006Z" }, + { url = "https://files.pythonhosted.org/packages/e7/86/ec93d495197f1006d7c9535e168fe763b3cc21928fb35c8f9ce08944b614/lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e", size = 22494, upload-time = "2023-01-04T19:56:07.705Z" }, + { url = "https://files.pythonhosted.org/packages/00/74/46a68f51457639c0cd79e385e2f49c0fa7324470997ac096108669c1e182/lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07", size = 22446, upload-time = "2023-01-04T19:56:09.108Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8d/8e0fbfeec6e51184326e0886443e44142ce22d89fa9e9c3152900e654fa0/lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a", size = 65699, upload-time = "2023-01-04T19:56:10.291Z" }, + { url = "https://files.pythonhosted.org/packages/c9/8f/c8aab72c72634de0c726a98a1e4c84a93ef20049ee0427c871214f6a58d5/lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59", size = 64765, upload-time = "2023-01-04T19:56:11.586Z" }, + { url = "https://files.pythonhosted.org/packages/51/28/5c6dfb51df2cbb6d771a3b0d009f1edeab01f5cb16303ce32764b01636c0/lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4", size = 68721, upload-time = "2023-01-04T19:56:12.698Z" }, + { url = "https://files.pythonhosted.org/packages/b0/78/78962cb6f6d31a952acbc54e7838a5a85d952144973bd6e7ad24323dd466/lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9", size = 68198, upload-time = "2023-01-04T19:56:14.107Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d7/81d466f2e69784bd416797824a2b8794aaf0b864a2390062ea197f06f0fc/lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586", size = 20725, upload-time = "2023-01-04T19:56:15.437Z" }, + { url = "https://files.pythonhosted.org/packages/8d/6d/10420823a979366bf43ca5e69433c0c588865883566b96b6e3ed5b51c1f8/lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb", size = 22495, upload-time = "2023-01-04T19:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/a7/51/6626c133e966698d53d65bcd90e34ad4986c5f0968c2328b3e9de51dbcf1/lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e", size = 22231, upload-time = "2023-01-04T19:56:17.674Z" }, + { url = "https://files.pythonhosted.org/packages/b1/80/2d9583fa8e5ac47ef183d811d26d833477b7ed02b64c17dd2ede68a3f9cf/lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8", size = 58307, upload-time = "2023-01-04T19:56:18.8Z" }, + { url = "https://files.pythonhosted.org/packages/11/fe/be1eb76d83f1b5242c492b410ce86c59db629c0b0f0f8e75018dfd955c30/lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2", size = 58494, upload-time = "2023-01-04T19:56:19.927Z" }, + { url = "https://files.pythonhosted.org/packages/db/92/284ab10a6d0f82da36a20d9c1464c30bb318d1a6dd0ae476de9f890e7abd/lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8", size = 62878, upload-time = "2023-01-04T19:56:21.922Z" }, + { url = "https://files.pythonhosted.org/packages/69/1f/51657d681711476287c9ff643428be0f9663addc1d341d4be1bad89290bd/lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda", size = 63119, upload-time = "2023-01-04T19:56:23.022Z" }, + { url = "https://files.pythonhosted.org/packages/11/04/fa820296cb937b378d801cdc81c19de06624cfed481c1b8a6b439255a188/lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734", size = 20705, upload-time = "2023-01-04T19:56:24.475Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4f/9ad496dc26a10ed9ab8f088732f08dc1f88488897d6c9ac5e3432a254c30/lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671", size = 22381, upload-time = "2023-01-04T19:56:26.195Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b6/84efe6e878e94f20cf9564ac3ede5e98d37c692b07080aef50cc4341052e/lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63", size = 22443, upload-time = "2023-01-04T19:56:27.5Z" }, + { url = "https://files.pythonhosted.org/packages/ed/9b/44c370c8bbba32fd0217b4f15ca99f750d669d653c7f1eefa051627710e8/lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171", size = 61360, upload-time = "2023-01-04T19:56:28.669Z" }, + { url = "https://files.pythonhosted.org/packages/4e/cb/aca3f4d89d3efbed724fd9504a96dafbe2d903ea908355a335acb110a5cd/lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be", size = 61382, upload-time = "2023-01-04T19:56:30.073Z" }, + { url = "https://files.pythonhosted.org/packages/70/e7/f3735f8e47cb29a207568c5b8d28d9f5673228789b66cb0c48d488a37f94/lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30", size = 64726, upload-time = "2023-01-04T19:56:31.231Z" }, + { url = "https://files.pythonhosted.org/packages/16/f2/e74981dedeb1a858cd5db9bcec81c4107da374249bc6894613472e01996f/lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11", size = 65076, upload-time = "2023-01-04T19:56:32.459Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a6/3c0a8b2ad6ce7af133ed54321b0ead5509303be3a80f98506af198e50cb7/lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82", size = 20729, upload-time = "2023-01-04T19:56:33.687Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f4/c5d6d771e70ec7a9483a98054e8a5f386eda5b18b6c96544d251558c6c92/lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b", size = 22488, upload-time = "2023-01-04T19:56:35.229Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/c1a67f76ec6923a8a8b1bc006b7cb3d195e386e03fe56e20fe38fce0321e/lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b", size = 22441, upload-time = "2023-01-04T19:56:36.615Z" }, + { url = "https://files.pythonhosted.org/packages/18/1b/04ac4490a62ae1916f88e629e74192ada97d74afc927453d005f003e5a8f/lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4", size = 62556, upload-time = "2023-01-04T19:56:38.26Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a4/cdd6f41a675a89ab584c78019a24adc533829764bcc85b0e24ed2678020c/lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006", size = 62078, upload-time = "2023-01-04T19:56:39.553Z" }, + { url = "https://files.pythonhosted.org/packages/82/ac/d079d3ad377ba72e29d16ac077f8626ad4d3f55369c93168d0b81153d9a2/lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494", size = 63596, upload-time = "2023-01-04T19:56:40.709Z" }, + { url = "https://files.pythonhosted.org/packages/5c/76/0b16dc53e9ee5b24c621d808f46cca11e5e86c602b6bcd6dc27f9504af5b/lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382", size = 63958, upload-time = "2023-01-04T19:56:41.839Z" }, + { url = "https://files.pythonhosted.org/packages/69/da/58391196d8a41fa8fa69b47e8a7893f279d369939e4994b3bc8648ff0433/lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821", size = 20724, upload-time = "2023-01-04T19:56:42.962Z" }, + { url = "https://files.pythonhosted.org/packages/fe/bb/0fcc8585ffb7285df94382e20b54d54ca62a1bcf594f6f18d8feb3fc3b98/lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f", size = 22488, upload-time = "2023-01-04T19:56:44.361Z" }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", +] +dependencies = [ + { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/28/e40d24d2e2eb23135f8533ad33d582359c7825623b1e022f9d460def7c05/platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731", size = 19914, upload-time = "2023-11-10T16:43:08.316Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/16/70be3b725073035aa5fc3229321d06e22e73e3e09f6af78dcfdf16c7636c/platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b", size = 17562, upload-time = "2023-11-10T16:43:06.949Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.8.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302, upload-time = "2024-09-17T19:06:50.688Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439, upload-time = "2024-09-17T19:06:49.212Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + +[[package]] +name = "pylint" +version = "2.13.9" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.7.2'", +] +dependencies = [ + { name = "astroid", version = "2.11.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7.2'" }, + { name = "colorama", marker = "python_full_version < '3.7.2' and sys_platform == 'win32'" }, + { name = "dill", version = "0.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7.2'" }, + { name = "isort", version = "5.11.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7.2'" }, + { name = "mccabe", marker = "python_full_version < '3.7.2'" }, + { name = "platformdirs", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7.2'" }, + { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7.2'" }, + { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.7.2'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/7c/ad5925aa1163b647ad10e81044288c93ef4317c96cd0a3d2b0f0b8059bd7/pylint-2.13.9.tar.gz", hash = "sha256:095567c96e19e6f57b5b907e67d265ff535e588fe26b12b5ebe1fc5645b2c731", size = 360270, upload-time = "2022-05-13T15:55:02.296Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/09/7b710f4aab53e3ccc0d9596557bf020c5ad06312e54ec1b60402ec9d694f/pylint-2.13.9-py3-none-any.whl", hash = "sha256:705c620d388035bdd9ff8b44c5bcdd235bfb49d276d488dd2c8ff1736aa42526", size = 438476, upload-time = "2022-05-13T15:55:00.721Z" }, +] + +[[package]] +name = "pylint" +version = "2.17.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.7.2' and python_full_version < '3.8'", +] +dependencies = [ + { name = "astroid", version = "2.15.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "colorama", marker = "python_full_version >= '3.7.2' and python_full_version < '3.8' and sys_platform == 'win32'" }, + { name = "dill", version = "0.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "isort", version = "5.11.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "mccabe", marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "platformdirs", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "tomlkit", version = "0.12.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, + { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.7.2' and python_full_version < '3.8'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/e9/21f9ce3e4b81eef011be070a29f8a5c193e2488ed8713a898baa4e8b3362/pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad", size = 434994, upload-time = "2023-09-30T21:25:10.328Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/49/cea450a83079445a84f16050e571a7c383d3f474b13c5caedfebd4e35def/pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87", size = 537178, upload-time = "2023-09-30T21:25:07.527Z" }, +] + +[[package]] +name = "pylint" +version = "3.2.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.8.*'", +] +dependencies = [ + { name = "astroid", version = "3.2.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "colorama", marker = "python_full_version == '3.8.*' and sys_platform == 'win32'" }, + { name = "dill", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "isort", version = "5.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "mccabe", marker = "python_full_version == '3.8.*'" }, + { name = "platformdirs", version = "4.3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "tomlkit", version = "0.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/e8/d59ce8e54884c9475ed6510685ef4311a10001674c28703b23da30f3b24d/pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e", size = 1511922, upload-time = "2024-08-31T14:26:26.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/4d/c73bc0fca447b918611985c325cd7017fb762050eb9c6ac6fa7d9ac6fbe4/pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b", size = 519906, upload-time = "2024-08-31T14:26:24.933Z" }, +] + +[[package]] +name = "pylint" +version = "3.3.9" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9.*'", +] +dependencies = [ + { name = "astroid", version = "3.3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "colorama", marker = "python_full_version == '3.9.*' and sys_platform == 'win32'" }, + { name = "dill", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "isort", version = "6.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "mccabe", marker = "python_full_version == '3.9.*'" }, + { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "tomlkit", version = "0.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/9d/81c84a312d1fa8133b0db0c76148542a98349298a01747ab122f9314b04e/pylint-3.3.9.tar.gz", hash = "sha256:d312737d7b25ccf6b01cc4ac629b5dcd14a0fcf3ec392735ac70f137a9d5f83a", size = 1525946, upload-time = "2025-10-05T18:41:43.786Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/a7/69460c4a6af7575449e615144aa2205b89408dc2969b87bc3df2f262ad0b/pylint-3.3.9-py3-none-any.whl", hash = "sha256:01f9b0462c7730f94786c283f3e52a1fbdf0494bbe0971a78d7277ef46a751e7", size = 523465, upload-time = "2025-10-05T18:41:41.766Z" }, +] + +[[package]] +name = "pylint" +version = "4.0.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "astroid", version = "4.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, + { name = "dill", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "isort", version = "7.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mccabe", marker = "python_full_version >= '3.10'" }, + { name = "platformdirs", version = "4.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "tomlkit", version = "0.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/d2/b081da1a8930d00e3fc06352a1d449aaf815d4982319fab5d8cdb2e9ab35/pylint-4.0.4.tar.gz", hash = "sha256:d9b71674e19b1c36d79265b5887bf8e55278cbe236c9e95d22dc82cf044fdbd2", size = 1571735, upload-time = "2025-11-30T13:29:04.315Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/92/d40f5d937517cc489ad848fc4414ecccc7592e4686b9071e09e64f5e378e/pylint-4.0.4-py3-none-any.whl", hash = "sha256:63e06a37d5922555ee2c20963eb42559918c20bd2b21244e4ef426e7c43b92e0", size = 536425, upload-time = "2025-11-30T13:29:02.53Z" }, +] + [[package]] name = "python-dotenv" version = "0.21.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.8'", + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", ] sdist = { url = "https://files.pythonhosted.org/packages/f5/d7/d548e0d5a68b328a8d69af833a861be415a17cb15ce3d8f0cd850073d2e1/python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49", size = 35930, upload-time = "2023-01-21T10:22:47.277Z" } wheels = [ @@ -267,13 +703,25 @@ name = "python-dotenv" version = "1.2.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, ] +[[package]] +name = "setuptools" +version = "68.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/98/5f896af066c128669229ff1aa81553ac14cfb3e5e74b6b44594132b8540e/setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235", size = 2194111, upload-time = "2023-06-19T15:53:05.082Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/42/be1c7bbdd83e1bfb160c94b9cafd8e25efc7400346cf7ccdbdb452c467fa/setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", size = 804037, upload-time = "2023-06-19T15:53:03.089Z" }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -283,6 +731,101 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] +[[package]] +name = "tomli" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164, upload-time = "2022-02-08T10:54:04.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757, upload-time = "2022-02-08T10:54:02.017Z" }, +] + +[[package]] +name = "tomli" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", + "python_full_version == '3.8.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, + { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, + { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.12.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.7.2' and python_full_version < '3.8'", +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/ab/18f4c8f2bec75eb1a7aebcc52cdb02ab04fd39ff7025bb1b1c7846cc45b8/tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c", size = 191420, upload-time = "2024-05-08T13:50:19.363Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/6d/b5406752c4e4ba86692b22fab0afed8b48f16bdde8f92e1d852976b61dc6/tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f", size = 37685, upload-time = "2024-05-08T13:50:17.343Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.13.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", + "python_full_version == '3.8.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, +] + [[package]] name = "tqdm" version = "4.67.1" @@ -295,12 +838,55 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] +[[package]] +name = "typed-ast" +version = "1.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/7e/a424029f350aa8078b75fd0d360a787a273ca753a678d1104c5fa4f3072a/typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd", size = 252841, upload-time = "2023-07-04T18:38:08.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/07/5defe18d4fc16281cd18c4374270abc430c3d852d8ac29b5db6599d45cfe/typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b", size = 223267, upload-time = "2023-07-04T18:37:00.129Z" }, + { url = "https://files.pythonhosted.org/packages/a0/5c/e379b00028680bfcd267d845cf46b60e76d8ac6f7009fd440d6ce030cc92/typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686", size = 208260, upload-time = "2023-07-04T18:37:03.069Z" }, + { url = "https://files.pythonhosted.org/packages/3b/99/5cc31ef4f3c80e1ceb03ed2690c7085571e3fbf119cbd67a111ec0b6622f/typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769", size = 842272, upload-time = "2023-07-04T18:37:04.916Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ed/b9b8b794b37b55c9247b1e8d38b0361e8158795c181636d34d6c11b506e7/typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04", size = 824651, upload-time = "2023-07-04T18:37:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/ca/59/dbbbe5a0e91c15d14a0896b539a5ed01326b0d468e75c1a33274d128d2d1/typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d", size = 854960, upload-time = "2023-07-04T18:37:08.474Z" }, + { url = "https://files.pythonhosted.org/packages/90/f0/0956d925f87bd81f6e0f8cf119eac5e5c8f4da50ca25bb9f5904148d4611/typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d", size = 839321, upload-time = "2023-07-04T18:37:10.417Z" }, + { url = "https://files.pythonhosted.org/packages/43/17/4bdece9795da6f3345c4da5667ac64bc25863617f19c28d81f350f515be6/typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02", size = 139380, upload-time = "2023-07-04T18:37:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/75/53/b685e10da535c7b3572735f8bea0d4abb35a04722a7d44ca9c163a0cf822/typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee", size = 223264, upload-time = "2023-07-04T18:37:13.637Z" }, + { url = "https://files.pythonhosted.org/packages/96/fd/fc8ccf19fc16a40a23e7c7802d0abc78c1f38f1abb6e2447c474f8a076d8/typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18", size = 208158, upload-time = "2023-07-04T18:37:15.141Z" }, + { url = "https://files.pythonhosted.org/packages/bf/9a/598e47f2c3ecd19d7f1bb66854d0d3ba23ffd93c846448790a92524b0a8d/typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88", size = 878366, upload-time = "2023-07-04T18:37:16.614Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/765e8bf8b24d0ed7b9fc669f6826c5bc3eb7412fc765691f59b83ae195b2/typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2", size = 860314, upload-time = "2023-07-04T18:37:18.215Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3c/4af750e6c673a0dd6c7b9f5b5e5ed58ec51a2e4e744081781c664d369dfa/typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9", size = 898108, upload-time = "2023-07-04T18:37:20.095Z" }, + { url = "https://files.pythonhosted.org/packages/03/8d/d0a4d1e060e1e8dda2408131a0cc7633fc4bc99fca5941dcb86c461dfe01/typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8", size = 881971, upload-time = "2023-07-04T18:37:21.912Z" }, + { url = "https://files.pythonhosted.org/packages/90/83/f28d2c912cd010a09b3677ac69d23181045eb17e358914ab739b7fdee530/typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b", size = 139286, upload-time = "2023-07-04T18:37:23.625Z" }, + { url = "https://files.pythonhosted.org/packages/d5/00/635353c31b71ed307ab020eff6baed9987da59a1b2ba489f885ecbe293b8/typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e", size = 222315, upload-time = "2023-07-04T18:37:36.008Z" }, + { url = "https://files.pythonhosted.org/packages/01/95/11be104446bb20212a741d30d40eab52a9cfc05ea34efa074ff4f7c16983/typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e", size = 793541, upload-time = "2023-07-04T18:37:37.614Z" }, + { url = "https://files.pythonhosted.org/packages/32/f1/75bd58fb1410cb72fbc6e8adf163015720db2c38844b46a9149c5ff6bf38/typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311", size = 778348, upload-time = "2023-07-04T18:37:39.332Z" }, + { url = "https://files.pythonhosted.org/packages/47/97/0bb4dba688a58ff9c08e63b39653e4bcaa340ce1bb9c1d58163e5c2c66f1/typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2", size = 809447, upload-time = "2023-07-04T18:37:41.017Z" }, + { url = "https://files.pythonhosted.org/packages/a8/cd/9a867f5a96d83a9742c43914e10d3a2083d8fe894ab9bf60fd467c6c497f/typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4", size = 796707, upload-time = "2023-07-04T18:37:42.625Z" }, + { url = "https://files.pythonhosted.org/packages/eb/06/73ca55ee5303b41d08920de775f02d2a3e1e59430371f5adf7fbb1a21127/typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431", size = 138403, upload-time = "2023-07-04T18:37:44.399Z" }, + { url = "https://files.pythonhosted.org/packages/19/e3/88b65e46643006592f39e0fdef3e29454244a9fdaa52acfb047dc68cae6a/typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a", size = 222951, upload-time = "2023-07-04T18:37:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/15/e0/182bdd9edb6c6a1c068cecaa87f58924a817f2807a0b0d940f578b3328df/typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437", size = 208247, upload-time = "2023-07-04T18:37:47.28Z" }, + { url = "https://files.pythonhosted.org/packages/8d/09/bba083f2c11746288eaf1859e512130420405033de84189375fe65d839ba/typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede", size = 861010, upload-time = "2023-07-04T18:37:48.847Z" }, + { url = "https://files.pythonhosted.org/packages/31/f3/38839df509b04fb54205e388fc04b47627377e0ad628870112086864a441/typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4", size = 840026, upload-time = "2023-07-04T18:37:50.631Z" }, + { url = "https://files.pythonhosted.org/packages/45/1e/aa5f1dae4b92bc665ae9a655787bb2fe007a881fa2866b0408ce548bb24c/typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6", size = 875615, upload-time = "2023-07-04T18:37:52.27Z" }, + { url = "https://files.pythonhosted.org/packages/94/88/71a1c249c01fbbd66f9f28648f8249e737a7fe19056c1a78e7b3b9250eb1/typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4", size = 858320, upload-time = "2023-07-04T18:37:54.23Z" }, + { url = "https://files.pythonhosted.org/packages/12/1e/19f53aad3984e351e6730e4265fde4b949a66c451e10828fdbc4dfb050f1/typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b", size = 139414, upload-time = "2023-07-04T18:37:55.912Z" }, + { url = "https://files.pythonhosted.org/packages/b1/88/6e7f36f5fab6fbf0586a2dd866ac337924b7d4796a4d1b2b04443a864faf/typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10", size = 223329, upload-time = "2023-07-04T18:37:57.344Z" }, + { url = "https://files.pythonhosted.org/packages/71/30/09d27e13824495547bcc665bd07afc593b22b9484f143b27565eae4ccaac/typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814", size = 208314, upload-time = "2023-07-04T18:37:59.073Z" }, + { url = "https://files.pythonhosted.org/packages/07/3d/564308b7a432acb1f5399933cbb1b376a1a64d2544b90f6ba91894674260/typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8", size = 840900, upload-time = "2023-07-04T18:38:00.562Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f4/262512d14f777ea3666a089e2675a9b1500a85b8329a36de85d63433fb0e/typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274", size = 823435, upload-time = "2023-07-04T18:38:02.532Z" }, + { url = "https://files.pythonhosted.org/packages/a1/25/b3ccb948166d309ab75296ac9863ebe2ff209fbc063f1122a2d3979e47c3/typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a", size = 853125, upload-time = "2023-07-04T18:38:04.128Z" }, + { url = "https://files.pythonhosted.org/packages/1c/09/012da182242f168bb5c42284297dcc08dc0a1b3668db5b3852aec467f56f/typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba", size = 837280, upload-time = "2023-07-04T18:38:05.968Z" }, + { url = "https://files.pythonhosted.org/packages/30/bd/c815051404c4293265634d9d3e292f04fcf681d0502a9484c38b8f224d04/typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155", size = 139486, upload-time = "2023-07-04T18:38:07.249Z" }, +] + [[package]] name = "typing-extensions" version = "4.7.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.8'", + "python_full_version >= '3.7.2' and python_full_version < '3.8'", + "python_full_version < '3.7.2'", ] sdist = { url = "https://files.pythonhosted.org/packages/3c/8b/0111dd7d6c1478bf83baa1cab85c686426c7a6274119aceb2bd9d35395ad/typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2", size = 72876, upload-time = "2023-07-02T14:20:55.045Z" } wheels = [ @@ -324,9 +910,89 @@ name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.9'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version == '3.10.*'", + "python_full_version == '3.9.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] + +[[package]] +name = "wrapt" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/4c/063a912e20bcef7124e0df97282a8af3ff3e4b603ce84c481d6d7346be0a/wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", size = 53972, upload-time = "2023-11-09T06:33:30.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/c6/5375258add3777494671d8cec27cdf5402abd91016dee24aa2972c61fedf/wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4", size = 37315, upload-time = "2023-11-09T06:31:34.487Z" }, + { url = "https://files.pythonhosted.org/packages/32/12/e11adfde33444986135d8881b401e4de6cbb4cced046edc6b464e6ad7547/wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", size = 38160, upload-time = "2023-11-09T06:31:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/70/7d/3dcc4a7e96f8d3e398450ec7703db384413f79bd6c0196e0e139055ce00f/wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", size = 80419, upload-time = "2023-11-09T06:31:38.956Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c4/8dfdc3c2f0b38be85c8d9fdf0011ebad2f54e40897f9549a356bebb63a97/wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", size = 72669, upload-time = "2023-11-09T06:31:40.741Z" }, + { url = "https://files.pythonhosted.org/packages/49/83/b40bc1ad04a868b5b5bcec86349f06c1ee1ea7afe51dc3e46131e4f39308/wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", size = 80271, upload-time = "2023-11-09T06:31:42.566Z" }, + { url = "https://files.pythonhosted.org/packages/19/d4/cd33d3a82df73a064c9b6401d14f346e1d2fb372885f0295516ec08ed2ee/wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", size = 84748, upload-time = "2023-11-09T06:31:44.718Z" }, + { url = "https://files.pythonhosted.org/packages/ef/58/2fde309415b5fa98fd8f5f4a11886cbf276824c4c64d45a39da342fff6fe/wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", size = 77522, upload-time = "2023-11-09T06:31:46.343Z" }, + { url = "https://files.pythonhosted.org/packages/07/44/359e4724a92369b88dbf09878a7cde7393cf3da885567ea898e5904049a3/wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", size = 84780, upload-time = "2023-11-09T06:31:48.006Z" }, + { url = "https://files.pythonhosted.org/packages/88/8f/706f2fee019360cc1da652353330350c76aa5746b4e191082e45d6838faf/wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", size = 35335, upload-time = "2023-11-09T06:31:49.517Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/548d23362e3002ebbfaefe649b833fa43f6ca37ac3e95472130c4b69e0b4/wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", size = 37528, upload-time = "2023-11-09T06:31:50.803Z" }, + { url = "https://files.pythonhosted.org/packages/fd/03/c188ac517f402775b90d6f312955a5e53b866c964b32119f2ed76315697e/wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", size = 37313, upload-time = "2023-11-09T06:31:52.168Z" }, + { url = "https://files.pythonhosted.org/packages/0f/16/ea627d7817394db04518f62934a5de59874b587b792300991b3c347ff5e0/wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", size = 38164, upload-time = "2023-11-09T06:31:53.522Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a7/f1212ba098f3de0fd244e2de0f8791ad2539c03bef6c05a9fcb03e45b089/wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", size = 80890, upload-time = "2023-11-09T06:31:55.247Z" }, + { url = "https://files.pythonhosted.org/packages/b7/96/bb5e08b3d6db003c9ab219c487714c13a237ee7dcc572a555eaf1ce7dc82/wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", size = 73118, upload-time = "2023-11-09T06:31:57.023Z" }, + { url = "https://files.pythonhosted.org/packages/6e/52/2da48b35193e39ac53cfb141467d9f259851522d0e8c87153f0ba4205fb1/wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", size = 80746, upload-time = "2023-11-09T06:31:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/11/fb/18ec40265ab81c0e82a934de04596b6ce972c27ba2592c8b53d5585e6bcd/wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", size = 85668, upload-time = "2023-11-09T06:31:59.992Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ef/0ecb1fa23145560431b970418dce575cfaec555ab08617d82eb92afc7ccf/wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", size = 78556, upload-time = "2023-11-09T06:32:01.942Z" }, + { url = "https://files.pythonhosted.org/packages/25/62/cd284b2b747f175b5a96cbd8092b32e7369edab0644c45784871528eb852/wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", size = 85712, upload-time = "2023-11-09T06:32:03.686Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a7/47b7ff74fbadf81b696872d5ba504966591a3468f1bc86bca2f407baef68/wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", size = 35327, upload-time = "2023-11-09T06:32:05.284Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c3/0084351951d9579ae83a3d9e38c140371e4c6b038136909235079f2e6e78/wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", size = 37523, upload-time = "2023-11-09T06:32:07.17Z" }, + { url = "https://files.pythonhosted.org/packages/92/17/224132494c1e23521868cdd57cd1e903f3b6a7ba6996b7b8f077ff8ac7fe/wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", size = 37614, upload-time = "2023-11-09T06:32:08.859Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d7/cfcd73e8f4858079ac59d9db1ec5a1349bc486ae8e9ba55698cc1f4a1dff/wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", size = 38316, upload-time = "2023-11-09T06:32:10.719Z" }, + { url = "https://files.pythonhosted.org/packages/7e/79/5ff0a5c54bda5aec75b36453d06be4f83d5cd4932cc84b7cb2b52cee23e2/wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", size = 86322, upload-time = "2023-11-09T06:32:12.592Z" }, + { url = "https://files.pythonhosted.org/packages/c4/81/e799bf5d419f422d8712108837c1d9bf6ebe3cb2a81ad94413449543a923/wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", size = 79055, upload-time = "2023-11-09T06:32:14.394Z" }, + { url = "https://files.pythonhosted.org/packages/62/62/30ca2405de6a20448ee557ab2cd61ab9c5900be7cbd18a2639db595f0b98/wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", size = 87291, upload-time = "2023-11-09T06:32:16.201Z" }, + { url = "https://files.pythonhosted.org/packages/49/4e/5d2f6d7b57fc9956bf06e944eb00463551f7d52fc73ca35cfc4c2cdb7aed/wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", size = 90374, upload-time = "2023-11-09T06:32:18.052Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/c2c21b44ff5b9bf14a83252a8b973fb84923764ff63db3e6dfc3895cf2e0/wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", size = 83896, upload-time = "2023-11-09T06:32:19.533Z" }, + { url = "https://files.pythonhosted.org/packages/14/26/93a9fa02c6f257df54d7570dfe8011995138118d11939a4ecd82cb849613/wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", size = 91738, upload-time = "2023-11-09T06:32:20.989Z" }, + { url = "https://files.pythonhosted.org/packages/a2/5b/4660897233eb2c8c4de3dc7cefed114c61bacb3c28327e64150dc44ee2f6/wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", size = 35568, upload-time = "2023-11-09T06:32:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/5c/cc/8297f9658506b224aa4bd71906447dea6bb0ba629861a758c28f67428b91/wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", size = 37653, upload-time = "2023-11-09T06:32:24.533Z" }, + { url = "https://files.pythonhosted.org/packages/47/cf/c2861bc5e0d5f4f277e1cefd7b3f8904794cc58469d35eaa82032a84e1c9/wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", size = 37069, upload-time = "2023-11-09T06:32:40.288Z" }, + { url = "https://files.pythonhosted.org/packages/54/39/04409d9fc89f77bce37b98545b6ee7247ad11df28373206536eea078a390/wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", size = 77587, upload-time = "2023-11-09T06:32:41.818Z" }, + { url = "https://files.pythonhosted.org/packages/26/dd/1ea7cb367962a6132ab163e7b2d270049e0f471f0238d0e55cfd27219721/wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", size = 69969, upload-time = "2023-11-09T06:32:43.405Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/896369f2550d1ecb5e776f532aada5e77e5e13f821045978cf3d7f3f236b/wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", size = 77483, upload-time = "2023-11-09T06:32:45.011Z" }, + { url = "https://files.pythonhosted.org/packages/bf/42/1241b88440ccf8adbf78c81c8899001459102031cc52668cc4e749d9987e/wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", size = 82832, upload-time = "2023-11-09T06:32:46.57Z" }, + { url = "https://files.pythonhosted.org/packages/03/60/67dbc0624f1c86cce6150c0b2e13d906009fd6d33128add60a8a2d23137d/wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", size = 75782, upload-time = "2023-11-09T06:32:47.924Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5f/574076e289c42e7c1c2abe944fd9dafb5adcb20b36577d4966ddef145539/wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", size = 82949, upload-time = "2023-11-09T06:32:49.259Z" }, + { url = "https://files.pythonhosted.org/packages/78/98/6307b4da5080432c5a37b69da92ae0582fd284441025014047e98a002ea1/wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", size = 35182, upload-time = "2023-11-09T06:32:51.53Z" }, + { url = "https://files.pythonhosted.org/packages/66/a5/50e6a2bd4cbf6671012771ec35085807a375da5e61540bc5f62de62ba955/wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", size = 37314, upload-time = "2023-11-09T06:32:52.835Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/d3bc95e75670ba15c5b25ecf07fc49941843e2678d777ca59339348d1c96/wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", size = 37320, upload-time = "2023-11-09T06:32:54.263Z" }, + { url = "https://files.pythonhosted.org/packages/72/b5/0c9be75f826c8e8d583a4ab312552d63d9f7c0768710146a22ac59bda4a9/wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", size = 38163, upload-time = "2023-11-09T06:32:55.819Z" }, + { url = "https://files.pythonhosted.org/packages/69/21/b2ba809bafc9b6265e359f9c259c6d9a52a16cf6be20c72d95e76da609dd/wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", size = 83535, upload-time = "2023-11-09T06:32:57.268Z" }, + { url = "https://files.pythonhosted.org/packages/58/43/d72e625edb5926483c9868214d25b5e7d5858ace6a80c9dfddfbadf4d8f9/wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", size = 75975, upload-time = "2023-11-09T06:32:58.668Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c6/56e718e2c58a4078518c14d97e531ef1e9e8a5c1ddafdc0d264a92be1a1a/wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", size = 83363, upload-time = "2023-11-09T06:33:00.529Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/589db6fa2d5d428b71716815bca8b39196fdaeea7c247a719ed2f93b0ab4/wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", size = 87739, upload-time = "2023-11-09T06:33:02.761Z" }, + { url = "https://files.pythonhosted.org/packages/c5/40/3eabe06c8dc54fada7364f34e8caa562efe3bf3f769bf3258de9c785a27f/wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", size = 80700, upload-time = "2023-11-09T06:33:05.225Z" }, + { url = "https://files.pythonhosted.org/packages/15/4e/081f59237b620a124b035f1229f55db40841a9339fdb8ef60b4decc44df9/wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", size = 87783, upload-time = "2023-11-09T06:33:07.929Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ad/9d26a33bc80444ff97b937f94611f3b986fd40f735823558dfdf05ef9db8/wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", size = 35332, upload-time = "2023-11-09T06:33:09.647Z" }, + { url = "https://files.pythonhosted.org/packages/01/db/4b29ba5f97d2a0aa97ec41eba1036b7c3eaf6e61e1f4639420cec2463a01/wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", size = 37524, upload-time = "2023-11-09T06:33:11.083Z" }, + { url = "https://files.pythonhosted.org/packages/70/cc/b92e1da2cad6a9f8ee481000ece07a35e3b24e041e60ff8b850c079f0ebf/wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", size = 37314, upload-time = "2023-11-09T06:33:12.535Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cc/3402bcc897978be00fef608cd9e3e39ec8869c973feeb5e1e277670e5ad2/wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", size = 38162, upload-time = "2023-11-09T06:33:14.102Z" }, + { url = "https://files.pythonhosted.org/packages/28/d3/4f079f649c515727c127c987b2ec2e0816b80d95784f2d28d1a57d2a1029/wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", size = 80235, upload-time = "2023-11-09T06:33:15.446Z" }, + { url = "https://files.pythonhosted.org/packages/a3/1c/226c2a4932e578a2241dcb383f425995f80224b446f439c2e112eb51c3a6/wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", size = 72553, upload-time = "2023-11-09T06:33:17.315Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e7/459a8a4f40f2fa65eb73cb3f339e6d152957932516d18d0e996c7ae2d7ae/wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", size = 80129, upload-time = "2023-11-09T06:33:18.858Z" }, + { url = "https://files.pythonhosted.org/packages/da/6f/6d0b3c4983f1fc764a422989dabc268ee87d937763246cd48aa92f1eed1e/wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", size = 84550, upload-time = "2023-11-09T06:33:20.267Z" }, + { url = "https://files.pythonhosted.org/packages/96/e8/27ef35cf61e5147c1c3abcb89cfbb8d691b2bb8364803fcc950140bc14d8/wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", size = 77352, upload-time = "2023-11-09T06:33:22.041Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ad/7a0766341081bfd9f18a7049e4d6d45586ae5c5bb0a640f05e2f558e849c/wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", size = 84626, upload-time = "2023-11-09T06:33:23.634Z" }, + { url = "https://files.pythonhosted.org/packages/09/43/b26852e9c45a1aac0d14b1080b25b612fa840ba99739c5fc55db07b7ce08/wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", size = 35327, upload-time = "2023-11-09T06:33:25.43Z" }, + { url = "https://files.pythonhosted.org/packages/74/f2/96ed140b08743f7f68d5bda35a2a589600781366c3da96f056043d258b1a/wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", size = 37526, upload-time = "2023-11-09T06:33:26.882Z" }, + { url = "https://files.pythonhosted.org/packages/ff/21/abdedb4cdf6ff41ebf01a74087740a709e2edb146490e4d9beea054b0b7a/wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", size = 23362, upload-time = "2023-11-09T06:33:28.271Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] From 66731e2fc1c6caa8478f1c77f0e72fad7930e8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=88=9A=28noham=29=C2=B2?= <100566912+NohamR@users.noreply.github.com> Date: Fri, 12 Dec 2025 18:30:08 +0100 Subject: [PATCH 2/5] Refactor CLI and client for improved structure and error handling Refactored the CLI to modularize argument parsing, logging, and output, and improved error handling and progress reporting. The client now uses dataclasses for file metadata, introduces custom exception classes, and provides a more robust upload interface supporting both file paths and file-like objects. Utilities were updated for clarity and type safety. --- src/gofilepy/__init__.py | 23 +++- src/gofilepy/cli.py | 276 +++++++++++++++++++++++++-------------- src/gofilepy/client.py | 248 ++++++++++++++++++++++++++--------- src/gofilepy/utils.py | 32 ++--- 4 files changed, 399 insertions(+), 180 deletions(-) diff --git a/src/gofilepy/__init__.py b/src/gofilepy/__init__.py index 05cd026..ef30960 100644 --- a/src/gofilepy/__init__.py +++ b/src/gofilepy/__init__.py @@ -1,7 +1,20 @@ -#!/usr/bin/env python3 +"""Top-level package exports for GofilePy.""" -from .client import GofileClient - -__version__ = "1.0.0" -__all__ = ["GofileClient"] +from .client import ( + GofileAPIError, + GofileClient, + GofileError, + GofileFile, + GofileNetworkError, + GofileUploadError, +) +__version__ = "1.1.2" +__all__ = [ + "GofileClient", + "GofileFile", + "GofileError", + "GofileAPIError", + "GofileNetworkError", + "GofileUploadError", +] diff --git a/src/gofilepy/cli.py b/src/gofilepy/cli.py index b37b103..3f557eb 100644 --- a/src/gofilepy/cli.py +++ b/src/gofilepy/cli.py @@ -1,130 +1,214 @@ -#!/usr/bin/env python3 +"""Command-line interface for uploading files to Gofile.""" + +from __future__ import annotations import argparse -import os import json import logging -from tqdm import tqdm -from dotenv import load_dotenv -from .client import GofileClient +import os +from typing import Callable, Dict, List, Optional -# Configure Logging -logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(message)s') +import httpx +from dotenv import load_dotenv +from tqdm import tqdm + +from .client import GofileClient, GofileError + +LOG_FORMAT = "[%(levelname)s] %(message)s" logger = logging.getLogger("gofilepy") -def main(): - load_dotenv() - parser = argparse.ArgumentParser(description="Gofile.io CLI Uploader (HTTPX Edition)") - - parser.add_argument("files", nargs='+', help="Files to upload") - - parser.add_argument("-s", "--to-single-folder", action="store_true", - help="Upload multiple files to the same folder.") - - parser.add_argument("-f", "--folder-id", type=str, default=None, - help="ID of an existing Gofile folder.") - - parser.add_argument("-vv", "--verbose", action="store_true", - help="Show detailed debug info.") - - parser.add_argument("--json", action="store_true", - help="Output result as JSON for scripts.") +def parse_arguments() -> argparse.Namespace: + """Return parsed CLI arguments.""" - args = parser.parse_args() + parser = argparse.ArgumentParser( + description="Gofile.io CLI Uploader (HTTPX Edition)", + ) + parser.add_argument("files", nargs="+", help="Files to upload") + parser.add_argument( + "-s", + "--to-single-folder", + action="store_true", + help="Upload multiple files to the same folder.", + ) + parser.add_argument( + "-f", + "--folder-id", + type=str, + default=None, + help="ID of an existing Gofile folder.", + ) + parser.add_argument( + "-vv", + "--verbose", + action="store_true", + help="Show detailed debug info.", + ) + parser.add_argument( + "--json", + action="store_true", + help="Output result as JSON for scripts.", + ) + return parser.parse_args() - # Log Level Handling - if args.verbose: - logger.setLevel(logging.DEBUG) - # HTTPX can be verbose, enable if needed - # logging.getLogger("httpx").setLevel(logging.DEBUG) - else: - logger.setLevel(logging.INFO) - logging.getLogger("httpx").setLevel(logging.WARNING) - # Token Logic - token = os.environ.get("GOFILE_TOKEN") +def configure_logging(verbose: bool) -> None: + """Configure logging for the CLI session.""" + + level = logging.DEBUG if verbose else logging.INFO + logging.basicConfig(level=level, format=LOG_FORMAT) + logger.setLevel(level) + httpx_logger = logging.getLogger("httpx") + httpx_logger.setLevel(logging.DEBUG if verbose else logging.WARNING) + + +def _log_token_state(token: Optional[str], json_mode: bool) -> None: + """Log whether a token was discovered for informational output.""" + + if json_mode: + return if token: masked_token = f"{token[:4]}..." - if not args.json: - logger.info(f"🔑 Token loaded: {masked_token}") + logger.info("🔑 Token loaded: %s", masked_token) else: - if not args.json: - logger.warning("⚠️ No GOFILE_TOKEN found in .env or environment. Running as Guest.") + logger.warning("⚠️ No GOFILE_TOKEN found in .env or environment. Running as Guest.") - client = GofileClient(token=token) - + +def _progress_callback_factory(progress_bar: Optional[tqdm]) -> Callable[[int], None]: + """Return a callback that updates the provided progress bar.""" + + def update(chunk_size: int, active_bar: Optional[tqdm] = progress_bar) -> None: + if active_bar: + active_bar.update(chunk_size) + + return update + + +def _create_progress_bar(filename: str, total: int, quiet: bool) -> Optional[tqdm]: + """Create a tqdm progress bar unless JSON mode is requested.""" + + if quiet: + return None + return tqdm(total=total, unit="B", unit_scale=True, desc=f"Uploading {filename}") + + +def _handle_upload_success( + data: Dict[str, object], + filename: str, +) -> Dict[str, object]: + """Normalize the success payload for presentation.""" + + return { + "file": filename, + "status": "success", + "downloadPage": data.get("downloadPage"), + "directLink": data.get("directLink", "N/A"), + "parentFolder": data.get("parentFolder"), + } + + +def _handle_upload_error(filename: str, error: Exception) -> Dict[str, object]: + """Normalize the error payload for presentation.""" + + return { + "file": filename, + "status": "error", + "message": str(error), + "errorType": error.__class__.__name__, + } + + +def _apply_guest_token(client: GofileClient, data: Dict[str, object]) -> None: + """Capture a guest token from the response so future uploads reuse the folder.""" + + guest_token = data.get("guestToken") + if guest_token and not client.token: + client.token = str(guest_token) + client.client.headers.update({"Authorization": f"Bearer {client.token}"}) + logger.debug("Guest token applied: %s", client.token) + + +def upload_files(args: argparse.Namespace, client: GofileClient) -> List[Dict[str, object]]: + """Upload each file sequentially and return the collected results.""" + + results: List[Dict[str, object]] = [] target_folder_id = args.folder_id - results = [] for file_path in args.files: if not os.path.exists(file_path): - res_err = {"file": file_path, "status": "error", "message": "File not found"} - results.append(res_err) - if not args.json: - logger.error(f"File not found: {file_path}") + logger.error("File not found: %s", file_path) + results.append({ + "file": file_path, + "status": "error", + "message": "File not found", + }) continue file_size = os.path.getsize(file_path) filename = os.path.basename(file_path) - - # Init Progress Bar (Only if not JSON mode) - pbar = None - if not args.json: - pbar = tqdm(total=file_size, unit='B', unit_scale=True, desc=f"Uploading {filename}") - - def progress_update(chunk_size): - if pbar: - pbar.update(chunk_size) + progress_bar = _create_progress_bar(filename, file_size, args.json) + progress_callback = _progress_callback_factory(progress_bar) try: data = client.upload_file( - file_path=file_path, + file_path=file_path, folder_id=target_folder_id, - callback=progress_update + callback=progress_callback, ) - - # --- Auto-Folder Management for Guests --- - # If we are in single folder mode and it's the first upload + if args.to_single_folder and target_folder_id is None: - if 'parentFolder' in data: - target_folder_id = data['parentFolder'] - logger.debug(f"Parent folder set to: {target_folder_id}") - - # If guest, capture the guestToken to write to the same folder next time - if 'guestToken' in data and not client.token: - client.token = data['guestToken'] - # Re-auth client with new token - client.client.headers.update({"Authorization": f"Bearer {client.token}"}) - logger.debug(f"Guest token applied: {client.token}") + parent_folder = data.get("parentFolder") + if parent_folder: + target_folder_id = str(parent_folder) + logger.debug("Parent folder set to: %s", target_folder_id) + _apply_guest_token(client, data) - results.append({ - "file": filename, - "status": "success", - "downloadPage": data.get("downloadPage"), - "directLink": data.get("directLink", "N/A"), # Sometimes available - "parentFolder": data.get("parentFolder") - }) - - except Exception as e: - err_msg = str(e) - results.append({"file": filename, "status": "error", "message": err_msg}) - if not args.json: - logger.error(f"Upload failed: {err_msg}") - finally: - if pbar: - pbar.close() - - # Output - if args.json: - print(json.dumps(results, indent=2)) - else: - print("\n--- Summary ---") - for res in results: - if res['status'] == 'success': - print(f"✅ {res['file']} -> {res['downloadPage']}") + results.append(_handle_upload_success(data, filename)) + except (GofileError, httpx.HTTPError, OSError) as error: + if logger.isEnabledFor(logging.DEBUG): + logger.exception("Upload failed for %s", filename) else: - print(f"❌ {res['file']} -> {res['message']}") + logger.error("Upload failed for %s: %s", filename, error) + results.append(_handle_upload_error(filename, error)) + finally: + if progress_bar: + progress_bar.close() + + return results + + +def output_results(results: List[Dict[str, object]], json_mode: bool) -> None: + """Display results in either JSON or human readable form.""" + + if json_mode: + print(json.dumps(results, indent=2)) + return + + print("\n--- Summary ---") + for result in results: + if result["status"] == "success": + print(f"✅ {result['file']} -> {result['downloadPage']}") + else: + print(f"❌ {result['file']} -> {result.get('message')}") + successes = sum(1 for res in results if res["status"] == "success") + failures = len(results) - successes + logger.info("Summary: %s succeeded, %s failed", successes, failures) + + +def main() -> None: + """Entrypoint for the CLI.""" + + load_dotenv() + args = parse_arguments() + configure_logging(args.verbose) + + token = os.environ.get("GOFILE_TOKEN") + _log_token_state(token, args.json) + + client = GofileClient(token=token) + results = upload_files(args, client) + output_results(results, args.json) + if __name__ == "__main__": main() diff --git a/src/gofilepy/client.py b/src/gofilepy/client.py index 65ff52a..2f392a2 100644 --- a/src/gofilepy/client.py +++ b/src/gofilepy/client.py @@ -1,101 +1,231 @@ -#!/usr/bin/env python3 +"""HTTP client for interacting with the Gofile API.""" + +from __future__ import annotations -import httpx import logging import os -from typing import Optional, List, Dict, Callable +from dataclasses import dataclass +from typing import Any, BinaryIO, Callable, Dict, List, Optional, Union + +import httpx + from .utils import ProgressFileReader logger = logging.getLogger(__name__) + +class GofileError(RuntimeError): + """Base exception for all Gofile client errors.""" + + def __init__(self, message: str, *, context: Optional[Dict[str, Any]] = None): + super().__init__(message) + self.context = context or {} + + +class GofileAPIError(GofileError): + """Raised when the Gofile API reports an error.""" + + +class GofileNetworkError(GofileError): + """Raised when the HTTP request fails before reaching the API.""" + + +class GofileUploadError(GofileError): + """Raised when the upload flow cannot complete.""" + + +@dataclass(slots=True) +class GofileFile: + """Represents a file returned by the Gofile API.""" + + name: str + page_link: str + file_id: str + parent_folder: str + raw: Dict[str, object] + + @classmethod + def from_data(cls, data: Dict[str, object]) -> "GofileFile": + """Create an instance from the API response payload.""" + + return cls( + name=str(data.get("fileName", "")), + page_link=str(data.get("downloadPage", "")), + file_id=str(data.get("fileId", "")), + parent_folder=str(data.get("parentFolder", "")), + raw=data, + ) + + def to_dict(self) -> Dict[str, object]: + """Return the original API payload as a new dict.""" + + return dict(self.raw) + + class GofileClient: + """Thin wrapper around Gofile's REST endpoints.""" + API_ROOT = "https://api.gofile.io" UPLOAD_SERVER_URL = "https://upload.gofile.io" def __init__(self, token: Optional[str] = None): + """Instantiate the client with an optional authentication token.""" + self.token = token - # Increase timeout for large API operations, though uploads handle their own timeout - self.client = httpx.Client(timeout=30.0) - + self.client = httpx.Client(timeout=30.0) + if self.token: - logger.debug(f"Initialized with token: {self.token[:4]}***") + logger.debug("Initialized with token: %s***", self.token[:4]) self.client.headers.update({"Authorization": f"Bearer {self.token}"}) - def _handle_response(self, response: httpx.Response) -> Dict: - logger.debug(f"Response Status: {response.status_code}") + def _handle_response(self, response: httpx.Response) -> Dict[str, object]: + """Validate HTTP responses and normalize API errors.""" + + logger.debug("Response status: %s", response.status_code) try: data = response.json() - logger.debug(f"Response Body: {data}") - except Exception: + logger.debug("Response body: %s", data) + except ValueError as exc: # httpx raises ValueError for invalid JSON error_text = response.text.strip() - logger.debug(f"Failed to parse JSON: {error_text}") + logger.debug("Failed to parse JSON: %s", error_text) response.raise_for_status() - return {} + raise GofileAPIError("Invalid JSON returned by Gofile API") from exc if data.get("status") != "ok": - logger.error(f"API Error: {data}") - raise Exception(f"Gofile API Error: {data.get('status')} - {data.get('data')}") - - return data.get("data", {}) + logger.error("API error payload: %s", data) + raise GofileAPIError( + f"Gofile API Error: {data.get('status')} - {data.get('data')}" + ) + + payload = data.get("data") + if not isinstance(payload, dict): + raise GofileAPIError("Gofile API returned unexpected payload structure") + return payload + + def _request( + self, method: str, url: str, *, context: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> Dict[str, object]: + """Execute an HTTP request and normalize errors.""" + + safe_context = context or {} + try: + logger.debug("HTTP %s %s | payload=%s", method, url, safe_context) + response = self.client.request(method, url, **kwargs) + except httpx.HTTPError as exc: + logger.error("HTTP %s %s failed: %s", method, url, exc) + raise GofileNetworkError( + f"Failed HTTP request to {url}", context={"method": method, **safe_context} + ) from exc + + return self._handle_response(response) + + @staticmethod + def _sanitize_metadata(metadata: Dict[str, str]) -> Dict[str, str]: + """Return a copy of request metadata with sensitive values redacted.""" + + redacted = dict(metadata) + if "token" in redacted: + redacted["token"] = "***REDACTED***" + return redacted def get_server(self) -> str: - """ - Gofile suggests using specific servers (availables in their doc), - but 'upload.gofile.io' uses DNS geo-routing automatically. - We stick to the best practice default. - """ + """Return the upload server, which leverages geo-aware routing.""" + return self.UPLOAD_SERVER_URL - def create_folder(self, parent_folder_id: str, folder_name: str) -> Dict: - logger.debug(f"Creating folder '{folder_name}' in '{parent_folder_id}'") + def create_folder(self, parent_folder_id: str, folder_name: str) -> Dict[str, object]: + """Create a folder under the provided parent folder.""" + + logger.debug("Creating folder '%s' in '%s'", folder_name, parent_folder_id) url = f"{self.API_ROOT}/contents/createFolder" payload = { "parentFolderId": parent_folder_id, - "folderName": folder_name + "folderName": folder_name, } - res = self.client.post(url, json=payload) - return self._handle_response(res) + return self._request("POST", url, json=payload, context=payload) - def delete_content(self, content_ids: List[str]) -> Dict: - logger.debug(f"Deleting content IDs: {content_ids}") + def delete_content(self, content_ids: List[str]) -> Dict[str, object]: + """Delete one or more items by their content IDs.""" + + logger.debug("Deleting content IDs: %s", content_ids) url = f"{self.API_ROOT}/contents" - # HTTPX needs 'content' or 'json' for DELETE requests explicitly if body is required - res = self.client.request("DELETE", url, json={"contentsId": ",".join(content_ids)}) - return self._handle_response(res) + payload = {"contentsId": ",".join(content_ids)} + return self._request("DELETE", url, json=payload, context=payload) + + def upload( + self, + file: Union[str, BinaryIO], + folder_id: Optional[str] = None, + callback: Optional[Callable[[int], None]] = None, + ) -> GofileFile: + """Upload a file object or file path to Gofile.""" - def upload_file(self, - file_path: str, - folder_id: Optional[str] = None, - callback: Optional[Callable[[int], None]] = None) -> Dict: - server_url = f"{self.get_server()}/uploadfile" - file_name = os.path.basename(file_path) - - # Prepare parameters - data = {} + data: Dict[str, str] = {} if self.token: data["token"] = self.token if folder_id: data["folderId"] = folder_id - # Use our custom ProgressFileReader - # If no callback is provided, we use a dummy lambda to avoid errors - progress_callback = callback if callback else lambda x: None - - logger.info(f"Starting upload: {file_name} -> {server_url}") - - # Open file using our wrapper - with ProgressFileReader(file_path, progress_callback) as f: - files = {'file': (file_name, f)} - - # Use a longer timeout for the upload specifically (None = infinite) - # This is crucial for 2000GB files - res = self.client.post( - server_url, - data=data, - files=files, - timeout=None + logger.debug("Upload metadata: %s", self._sanitize_metadata(data)) + + progress_callback = callback or (lambda _chunk: None) + + if isinstance(file, str): + file_name = os.path.basename(file) + logger.info("Starting upload: %s -> %s", file_name, server_url) + with open(file, "rb") as file_handle: + wrapped_file = ProgressFileReader(file_handle, progress_callback) + response = self._post_upload( + server_url, + data=data, + files={"file": (file_name, wrapped_file)}, + ) + else: + file_name = getattr(file, "name", "uploaded_file") + if hasattr(file_name, "__fspath__"): + file_name = os.path.basename(file_name) # type: ignore[arg-type] + elif "/" in str(file_name) or "\\" in str(file_name): + file_name = os.path.basename(str(file_name)) + + logger.info("Starting upload: %s -> %s", file_name, server_url) + files = {"file": (file_name, file)} + response = self._post_upload( + server_url, + data=data, + files=files, ) - return self._handle_response(res) + response_data = self._handle_response(response) + logger.info("Upload finished: %s", file_name) + return GofileFile.from_data(response_data) + + def _post_upload( + self, + url: str, + *, + data: Dict[str, str], + files: Dict[str, Any], + ) -> httpx.Response: + """Issue the actual upload request with improved error context.""" + + try: + return self.client.post(url, data=data, files=files, timeout=None) + except httpx.TimeoutException as exc: + logger.error("Upload timed out at %s", url) + raise GofileUploadError("Upload timed out", context={"url": url}) from exc + except httpx.HTTPError as exc: + logger.error("HTTP error while uploading to %s: %s", url, exc) + raise GofileUploadError("Upload failed", context={"url": url}) from exc + + def upload_file( + self, + file_path: str, + folder_id: Optional[str] = None, + callback: Optional[Callable[[int], None]] = None, + ) -> Dict[str, object]: + """Compatibility helper that mirrors the legacy API.""" + + result = self.upload(file_path, folder_id, callback) + return result.to_dict() diff --git a/src/gofilepy/utils.py b/src/gofilepy/utils.py index 03157fa..9a7eeeb 100644 --- a/src/gofilepy/utils.py +++ b/src/gofilepy/utils.py @@ -1,28 +1,20 @@ -#!/usr/bin/env python3 +"""Utility helpers for GofilePy.""" + +from __future__ import annotations -import typing import io +from typing import BinaryIO, Callable + class ProgressFileReader(io.BufferedReader): - """ - Wraps a file object to trigger a callback when data is read. - This allows monitoring upload progress in httpx without loading the file into RAM. - """ - def __init__(self, filename: str, callback: typing.Callable[[int], None]): - self._f = open(filename, 'rb') - self._callback = callback - # Get file size for verification if needed, or just standard init - super().__init__(self._f) + """Buffered reader that reports read progress through a callback.""" - def read(self, size: int = -1) -> bytes: - # Read the chunk from disk - chunk = self._f.read(size) - # Update the progress bar with the length of the chunk read + def __init__(self, file_obj: BinaryIO, callback: Callable[[int], None]): + self._callback = callback + super().__init__(file_obj) + + def read(self, size: int = -1) -> bytes: # type: ignore[override] + chunk = super().read(size) if chunk: self._callback(len(chunk)) return chunk - - def close(self) -> None: - if hasattr(self, '_f'): - self._f.close() - From 9a4c0b177652be23b6e10ef42fc62d9c45b755e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=88=9A=28noham=29=C2=B2?= <100566912+NohamR@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:44:20 +0100 Subject: [PATCH 3/5] Add download support to CLI and client Implemented file and folder download functionality in the CLI and GofileClient, including new CLI arguments for downloading by URL or content ID and specifying output directory. --- README.md | 34 +++++++- src/gofilepy/cli.py | 172 +++++++++++++++++++++++++++++++++++--- src/gofilepy/client.py | 82 ++++++++++++++++++ test_download.py | 18 ++++ test.py => test_upload.py | 2 +- 5 files changed, 295 insertions(+), 13 deletions(-) create mode 100644 test_download.py rename test.py => test_upload.py (79%) diff --git a/README.md b/README.md index 0c884f1..5bb67c6 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,11 @@ It supports the free API tiers, streaming uploads (low memory usage for large fi ## Features - **Streaming Uploads**: Upload 100GB+ files without loading them into RAM. +- **Download Support**: Download files from Gofile URLs or content IDs. - **Folder Management**: Upload to specific folders or create new ones automatically. - **Script Ready**: JSON output mode for easy parsing in pipelines. - **Free Tier Support**: Handles Guest accounts and Standard tokens. -- **Progress Bar**: Visual feedback for long uploads. +- **Progress Bar**: Visual feedback for long uploads and downloads. ## Installation @@ -68,6 +69,24 @@ Upload multiple files. The first file creates a folder, and the rest are uploade gofilepy -s part1.rar part2.rar part3.rar ``` +### Download Files +Download files from a Gofile URL or content ID. + +Download from URL: +```bash +gofilepy -d https://gofile.io/d/GxHNKL +``` + +Download from content ID: +```bash +gofilepy -d GxHNKL +``` + +Download to specific directory +```bash +gofilepy -d GxHNKL -o ./downloads +``` + ### Scripting Mode (JSON Output) Use `--json` to suppress human-readable text and output a JSON array. @@ -87,6 +106,8 @@ gofilepy -vv big_file.iso You can use `gofilepy` in your own Python scripts. +### Upload Files + ```python from gofilepy import GofileClient @@ -97,6 +118,17 @@ print(file.name) print(file.page_link) # View and download file at this link ``` +### Download Files + +```python +from gofilepy import GofileClient + +client = GofileClient() +contents = client.get_contents("GxHNKL") +print("Folder contents:") +print(contents) +``` + ## Development For contributors and developers: diff --git a/src/gofilepy/cli.py b/src/gofilepy/cli.py index 3f557eb..6950837 100644 --- a/src/gofilepy/cli.py +++ b/src/gofilepy/cli.py @@ -24,7 +24,21 @@ def parse_arguments() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Gofile.io CLI Uploader (HTTPX Edition)", ) - parser.add_argument("files", nargs="+", help="Files to upload") + parser.add_argument("files", nargs="*", help="Files to upload") + parser.add_argument( + "-d", + "--download", + type=str, + metavar="URL", + help="Download files from a Gofile URL (folder or content ID).", + ) + parser.add_argument( + "-o", + "--output", + type=str, + default=".", + help="Output directory for downloads (default: current directory).", + ) parser.add_argument( "-s", "--to-single-folder", @@ -84,12 +98,12 @@ def _progress_callback_factory(progress_bar: Optional[tqdm]) -> Callable[[int], return update -def _create_progress_bar(filename: str, total: int, quiet: bool) -> Optional[tqdm]: +def _create_progress_bar(filename: str, total: int, quiet: bool, mode: str = "Uploading") -> Optional[tqdm]: """Create a tqdm progress bar unless JSON mode is requested.""" if quiet: return None - return tqdm(total=total, unit="B", unit_scale=True, desc=f"Uploading {filename}") + return tqdm(total=total, unit="B", unit_scale=True, desc=f"{mode} {filename}") def _handle_upload_success( @@ -177,7 +191,129 @@ def upload_files(args: argparse.Namespace, client: GofileClient) -> List[Dict[st return results -def output_results(results: List[Dict[str, object]], json_mode: bool) -> None: +def extract_content_id(url_or_id: str) -> str: + """Extract the content ID from a Gofile URL or return the ID as-is.""" + + # Handle URLs like https://gofile.io/d/nC5ulQ or direct IDs + if "gofile.io/d/" in url_or_id: + return url_or_id.split("gofile.io/d/")[-1].split("?")[0].split("/")[0] + elif "gofile.io" in url_or_id: + # Handle other URL patterns + parts = url_or_id.rstrip("/").split("/") + return parts[-1].split("?")[0] + return url_or_id + + +def download_files(args: argparse.Namespace, client: GofileClient) -> List[Dict[str, object]]: + """Download files from a Gofile URL or content ID.""" + + results: List[Dict[str, object]] = [] + content_id = extract_content_id(args.download) + + try: + # Fetch content information + logger.info("Fetching content information for: %s", content_id) + response = client.get_contents(content_id) + + # The response is already the "data" object from get_contents + data = response if isinstance(response, dict) else {} + if not isinstance(data, dict): + raise GofileError("Invalid response structure from API") + + content_type = data.get("type") + + if content_type == "file": + # Single file download + file_name = str(data.get("name", "downloaded_file")) + download_link = str(data.get("link", "")) + + if not download_link: + raise GofileError("No download link found in response") + + output_path = os.path.join(args.output, file_name) + file_size = int(data.get("size", 0)) + + progress_bar = _create_progress_bar(file_name, file_size, args.json, mode="Downloading") + progress_callback = _progress_callback_factory(progress_bar) + + try: + client.download_file(download_link, output_path, progress_callback) + results.append({ + "file": file_name, + "status": "success", + "path": output_path, + "size": file_size, + }) + except (GofileError, httpx.HTTPError, OSError) as error: + logger.error("Download failed for %s: %s", file_name, error) + results.append(_handle_upload_error(file_name, error)) + finally: + if progress_bar: + progress_bar.close() + + elif content_type == "folder": + # Multiple files in folder + children = data.get("children", {}) + if not isinstance(children, dict): + raise GofileError("Invalid children structure in folder response") + + logger.info("Found %s file(s) in folder", len(children)) + + for child_id, child_data in children.items(): + if not isinstance(child_data, dict): + continue + + child_type = child_data.get("type") + if child_type != "file": + logger.debug("Skipping non-file item: %s", child_id) + continue + + file_name = str(child_data.get("name", f"file_{child_id}")) + download_link = str(child_data.get("link", "")) + + if not download_link: + logger.warning("No download link for %s, skipping", file_name) + continue + + output_path = os.path.join(args.output, file_name) + file_size = int(child_data.get("size", 0)) + + progress_bar = _create_progress_bar(file_name, file_size, args.json, mode="Downloading") + progress_callback = _progress_callback_factory(progress_bar) + + try: + client.download_file(download_link, output_path, progress_callback) + results.append({ + "file": file_name, + "status": "success", + "path": output_path, + "size": file_size, + }) + except (GofileError, httpx.HTTPError, OSError) as error: + logger.error("Download failed for %s: %s", file_name, error) + results.append(_handle_upload_error(file_name, error)) + finally: + if progress_bar: + progress_bar.close() + else: + raise GofileError(f"Unknown content type: {content_type}") + + except (GofileError, httpx.HTTPError) as error: + if logger.isEnabledFor(logging.DEBUG): + logger.exception("Failed to download from %s", content_id) + else: + logger.error("Failed to download from %s: %s", content_id, error) + results.append({ + "content_id": content_id, + "status": "error", + "message": str(error), + "errorType": error.__class__.__name__, + }) + + return results + + +def output_results(results: List[Dict[str, object]], json_mode: bool, is_download: bool = False) -> None: """Display results in either JSON or human readable form.""" if json_mode: @@ -187,9 +323,12 @@ def output_results(results: List[Dict[str, object]], json_mode: bool) -> None: print("\n--- Summary ---") for result in results: if result["status"] == "success": - print(f"✅ {result['file']} -> {result['downloadPage']}") + if is_download: + print(f"✅ {result['file']} -> {result.get('path')}") + else: + print(f"✅ {result['file']} -> {result.get('downloadPage')}") else: - print(f"❌ {result['file']} -> {result.get('message')}") + print(f"❌ {result.get('file', result.get('content_id', 'unknown'))} -> {result.get('message')}") successes = sum(1 for res in results if res["status"] == "success") failures = len(results) - successes logger.info("Summary: %s succeeded, %s failed", successes, failures) @@ -203,11 +342,22 @@ def main() -> None: configure_logging(args.verbose) token = os.environ.get("GOFILE_TOKEN") - _log_token_state(token, args.json) - - client = GofileClient(token=token) - results = upload_files(args, client) - output_results(results, args.json) + + # Check if we're in download mode or upload mode + if args.download: + _log_token_state(token, args.json) + client = GofileClient(token=token) + results = download_files(args, client) + output_results(results, args.json, is_download=True) + elif args.files: + _log_token_state(token, args.json) + client = GofileClient(token=token) + results = upload_files(args, client) + output_results(results, args.json, is_download=False) + else: + logger.error("No files specified for upload and no download URL provided.") + logger.error("Use -d/--download to download or provide files to upload.") + raise SystemExit(1) if __name__ == "__main__": diff --git a/src/gofilepy/client.py b/src/gofilepy/client.py index 2f392a2..d8697ed 100644 --- a/src/gofilepy/client.py +++ b/src/gofilepy/client.py @@ -229,3 +229,85 @@ class GofileClient: result = self.upload(file_path, folder_id, callback) return result.to_dict() + + def create_guest_account(self) -> Dict[str, Any]: + """Create a guest account and return the token.""" + + logger.debug("Creating guest account") + url = f"{self.API_ROOT}/accounts" + response = self._request("POST", url, context={"action": "create_guest"}) + + if "token" in response: + self.token = str(response["token"]) + self.client.headers.update({"Authorization": f"Bearer {self.token}"}) + logger.debug("Guest account created with token: %s***", self.token[:4]) + + return response + + def get_contents(self, content_id: str) -> Dict[str, Any]: + """Fetch information about a content ID (folder or file).""" + + # If we don't have a token, create a guest account first + if not self.token: + logger.debug("No token available, creating guest account") + self.create_guest_account() + + logger.debug("Fetching contents for: %s", content_id) + # Add query parameters and website token header as shown in the API + url = f"{self.API_ROOT}/contents/{content_id}" + params = { + "contentFilter": "", + "page": "1", + "pageSize": "1000", + "sortField": "name", + "sortDirection": "1" + } + headers = { + "x-website-token": "4fd6sg89d7s6" # to avoid error-notPremium + } + return self._request("GET", url, params=params, headers=headers, context={"content_id": content_id}) + + def download_file( + self, + download_url: str, + output_path: str, + callback: Optional[Callable[[int], None]] = None, + ) -> None: + """Download a file from the provided direct link.""" + + logger.info("Starting download: %s -> %s", download_url, output_path) + + cookies = {} + if self.token: + cookies["accountToken"] = self.token + logger.debug("Using accountToken cookie for download") + + try: + with self.client.stream("GET", download_url, cookies=cookies, timeout=None) as response: + response.raise_for_status() + + total_size = int(response.headers.get("content-length", 0)) + logger.debug("File size: %s bytes", total_size) + + os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True) + + with open(output_path, "wb") as f: + for chunk in response.iter_bytes(chunk_size=8192): + if chunk: + f.write(chunk) + if callback: + callback(len(chunk)) + + logger.info("Download complete: %s", output_path) + except httpx.HTTPError as exc: + logger.error("Download failed for %s: %s", download_url, exc) + raise GofileNetworkError( + f"Failed to download from {download_url}", + context={"url": download_url, "output": output_path} + ) from exc + except OSError as exc: + logger.error("Failed to write file %s: %s", output_path, exc) + raise GofileError( + f"Failed to write file to {output_path}", + context={"output": output_path} + ) from exc diff --git a/test_download.py b/test_download.py new file mode 100644 index 0000000..0431057 --- /dev/null +++ b/test_download.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +"""Test script for download functionality.""" + +from gofilepy import GofileClient + +# Test downloading from the folder URL +client = GofileClient() + +# Get folder contents +contents = client.get_contents("GxHNKL") +print("Folder contents:") +print(contents) + +# You can also download programmatically like this: +# client.download_file( +# download_url="https://store-eu-par-6.gofile.io/download/web/folder-id/file.py", +# output_path="./downloaded_test.py" +# ) diff --git a/test.py b/test_upload.py similarity index 79% rename from test.py rename to test_upload.py index be4f14e..b6e00bd 100644 --- a/test.py +++ b/test_upload.py @@ -2,6 +2,6 @@ from gofilepy import GofileClient client = GofileClient() # client = GofileClient(token="YOUR_TOKEN_HERE") # Optional token for private uploads -file = client.upload(file=open("./test.py", "rb")) +file = client.upload(file=open("./test_upload.py", "rb")) print(file.name) print(file.page_link) # View and download file at this link \ No newline at end of file From ace62ec3f82ed4339ce45c9877da75924eb0935d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=88=9A=28noham=29=C2=B2?= <100566912+NohamR@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:49:42 +0100 Subject: [PATCH 4/5] Improve code structure Refactored the download_files function in cli.py to separate single file and folder download logic into helper functions, improving readability and maintainability. --- src/gofilepy/cli.py | 188 +++++++++++++++++++++++------------------ src/gofilepy/client.py | 23 ++--- test_download.py | 2 +- 3 files changed, 122 insertions(+), 91 deletions(-) diff --git a/src/gofilepy/cli.py b/src/gofilepy/cli.py index 6950837..33bd2a4 100644 --- a/src/gofilepy/cli.py +++ b/src/gofilepy/cli.py @@ -6,7 +6,7 @@ import argparse import json import logging import os -from typing import Callable, Dict, List, Optional +from typing import Any, Callable, Dict, List, Optional import httpx from dotenv import load_dotenv @@ -98,7 +98,9 @@ def _progress_callback_factory(progress_bar: Optional[tqdm]) -> Callable[[int], return update -def _create_progress_bar(filename: str, total: int, quiet: bool, mode: str = "Uploading") -> Optional[tqdm]: +def _create_progress_bar( + filename: str, total: int, quiet: bool, mode: str = "Uploading" +) -> Optional[tqdm]: """Create a tqdm progress bar unless JSON mode is requested.""" if quiet: @@ -197,123 +199,148 @@ def extract_content_id(url_or_id: str) -> str: # Handle URLs like https://gofile.io/d/nC5ulQ or direct IDs if "gofile.io/d/" in url_or_id: return url_or_id.split("gofile.io/d/")[-1].split("?")[0].split("/")[0] - elif "gofile.io" in url_or_id: + if "gofile.io" in url_or_id: # Handle other URL patterns parts = url_or_id.rstrip("/").split("/") return parts[-1].split("?")[0] return url_or_id +def _download_single_file( + client: GofileClient, + file_name: str, + download_link: str, + output_path: str, + file_size: int, + *, + quiet: bool +) -> Dict[str, object]: + """Download a single file and return the result.""" + progress_bar = _create_progress_bar(file_name, file_size, quiet, mode="Downloading") + progress_callback = _progress_callback_factory(progress_bar) + + try: + client.download_file(download_link, output_path, progress_callback) + return { + "file": file_name, + "status": "success", + "path": output_path, + "size": file_size, + } + except (GofileError, httpx.HTTPError, OSError) as error: + logger.error("Download failed for %s: %s", file_name, error) + return _handle_upload_error(file_name, error) + finally: + if progress_bar: + progress_bar.close() + + +def _process_file_data( + client: GofileClient, + file_name: str, + file_data: Dict[str, Any], + output_dir: str, + quiet: bool +) -> Dict[str, object]: + """Process and download a single file from file data.""" + download_link = str(file_data.get("link", "")) + + if not download_link: + logger.warning("No download link for %s, skipping", file_name) + return _handle_upload_error(file_name, GofileError("No download link")) + + output_path = os.path.join(output_dir, file_name) + file_size = int(file_data.get("size", 0)) + + return _download_single_file( + client, file_name, download_link, output_path, file_size, quiet=quiet + ) + + +def _download_folder_contents( + client: GofileClient, + children: Dict[str, Any], + output_dir: str, + quiet: bool +) -> List[Dict[str, object]]: + """Download all files from a folder.""" + results: List[Dict[str, object]] = [] + logger.info("Found %s file(s) in folder", len(children)) + + for child_id, child_data in children.items(): + if not isinstance(child_data, dict): + continue + + child_type = child_data.get("type") + if child_type != "file": + logger.debug("Skipping non-file item: %s", child_id) + continue + + file_name = str(child_data.get("name", f"file_{child_id}")) + result = _process_file_data(client, file_name, child_data, output_dir, quiet) + results.append(result) + + return results + + def download_files(args: argparse.Namespace, client: GofileClient) -> List[Dict[str, object]]: """Download files from a Gofile URL or content ID.""" - - results: List[Dict[str, object]] = [] content_id = extract_content_id(args.download) - + try: # Fetch content information logger.info("Fetching content information for: %s", content_id) response = client.get_contents(content_id) - + # The response is already the "data" object from get_contents data = response if isinstance(response, dict) else {} if not isinstance(data, dict): raise GofileError("Invalid response structure from API") - + content_type = data.get("type") - + if content_type == "file": # Single file download file_name = str(data.get("name", "downloaded_file")) download_link = str(data.get("link", "")) - + if not download_link: raise GofileError("No download link found in response") - + output_path = os.path.join(args.output, file_name) file_size = int(data.get("size", 0)) - - progress_bar = _create_progress_bar(file_name, file_size, args.json, mode="Downloading") - progress_callback = _progress_callback_factory(progress_bar) - - try: - client.download_file(download_link, output_path, progress_callback) - results.append({ - "file": file_name, - "status": "success", - "path": output_path, - "size": file_size, - }) - except (GofileError, httpx.HTTPError, OSError) as error: - logger.error("Download failed for %s: %s", file_name, error) - results.append(_handle_upload_error(file_name, error)) - finally: - if progress_bar: - progress_bar.close() - - elif content_type == "folder": + + result = _download_single_file( + client, file_name, download_link, output_path, file_size, quiet=args.json + ) + return [result] + + if content_type == "folder": # Multiple files in folder children = data.get("children", {}) if not isinstance(children, dict): raise GofileError("Invalid children structure in folder response") - - logger.info("Found %s file(s) in folder", len(children)) - - for child_id, child_data in children.items(): - if not isinstance(child_data, dict): - continue - - child_type = child_data.get("type") - if child_type != "file": - logger.debug("Skipping non-file item: %s", child_id) - continue - - file_name = str(child_data.get("name", f"file_{child_id}")) - download_link = str(child_data.get("link", "")) - - if not download_link: - logger.warning("No download link for %s, skipping", file_name) - continue - - output_path = os.path.join(args.output, file_name) - file_size = int(child_data.get("size", 0)) - - progress_bar = _create_progress_bar(file_name, file_size, args.json, mode="Downloading") - progress_callback = _progress_callback_factory(progress_bar) - - try: - client.download_file(download_link, output_path, progress_callback) - results.append({ - "file": file_name, - "status": "success", - "path": output_path, - "size": file_size, - }) - except (GofileError, httpx.HTTPError, OSError) as error: - logger.error("Download failed for %s: %s", file_name, error) - results.append(_handle_upload_error(file_name, error)) - finally: - if progress_bar: - progress_bar.close() - else: - raise GofileError(f"Unknown content type: {content_type}") - + + return _download_folder_contents(client, children, args.output, args.json) + + raise GofileError(f"Unknown content type: {content_type}") + except (GofileError, httpx.HTTPError) as error: if logger.isEnabledFor(logging.DEBUG): logger.exception("Failed to download from %s", content_id) else: logger.error("Failed to download from %s: %s", content_id, error) - results.append({ + return [{ "content_id": content_id, "status": "error", "message": str(error), "errorType": error.__class__.__name__, - }) - - return results + }] -def output_results(results: List[Dict[str, object]], json_mode: bool, is_download: bool = False) -> None: +def output_results( + results: List[Dict[str, object]], json_mode: bool, is_download: bool = False +) -> None: """Display results in either JSON or human readable form.""" if json_mode: @@ -328,7 +355,8 @@ def output_results(results: List[Dict[str, object]], json_mode: bool, is_downloa else: print(f"✅ {result['file']} -> {result.get('downloadPage')}") else: - print(f"❌ {result.get('file', result.get('content_id', 'unknown'))} -> {result.get('message')}") + error_name = result.get('file', result.get('content_id', 'unknown')) + print(f"❌ {error_name} -> {result.get('message')}") successes = sum(1 for res in results if res["status"] == "success") failures = len(results) - successes logger.info("Summary: %s succeeded, %s failed", successes, failures) @@ -342,7 +370,7 @@ def main() -> None: configure_logging(args.verbose) token = os.environ.get("GOFILE_TOKEN") - + # Check if we're in download mode or upload mode if args.download: _log_token_state(token, args.json) diff --git a/src/gofilepy/client.py b/src/gofilepy/client.py index d8697ed..6fe5712 100644 --- a/src/gofilepy/client.py +++ b/src/gofilepy/client.py @@ -236,12 +236,12 @@ class GofileClient: logger.debug("Creating guest account") url = f"{self.API_ROOT}/accounts" response = self._request("POST", url, context={"action": "create_guest"}) - + if "token" in response: self.token = str(response["token"]) self.client.headers.update({"Authorization": f"Bearer {self.token}"}) logger.debug("Guest account created with token: %s***", self.token[:4]) - + return response def get_contents(self, content_id: str) -> Dict[str, Any]: @@ -263,9 +263,12 @@ class GofileClient: "sortDirection": "1" } headers = { - "x-website-token": "4fd6sg89d7s6" # to avoid error-notPremium + # to avoid error-notPremium + "x-website-token": "4fd6sg89d7s6" } - return self._request("GET", url, params=params, headers=headers, context={"content_id": content_id}) + return self._request( + "GET", url, params=params, headers=headers, context={"content_id": content_id} + ) def download_file( self, @@ -276,28 +279,28 @@ class GofileClient: """Download a file from the provided direct link.""" logger.info("Starting download: %s -> %s", download_url, output_path) - + cookies = {} if self.token: cookies["accountToken"] = self.token logger.debug("Using accountToken cookie for download") - + try: with self.client.stream("GET", download_url, cookies=cookies, timeout=None) as response: response.raise_for_status() - + total_size = int(response.headers.get("content-length", 0)) logger.debug("File size: %s bytes", total_size) - + os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True) - + with open(output_path, "wb") as f: for chunk in response.iter_bytes(chunk_size=8192): if chunk: f.write(chunk) if callback: callback(len(chunk)) - + logger.info("Download complete: %s", output_path) except httpx.HTTPError as exc: logger.error("Download failed for %s: %s", download_url, exc) diff --git a/test_download.py b/test_download.py index 0431057..02b3b15 100644 --- a/test_download.py +++ b/test_download.py @@ -7,7 +7,7 @@ from gofilepy import GofileClient client = GofileClient() # Get folder contents -contents = client.get_contents("GxHNKL") +contents = client.get_contents("QUo3a5") print("Folder contents:") print(contents) From 00bc2572c96b238287e1f1391306c2cbc012b6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=88=9A=28noham=29=C2=B2?= <100566912+NohamR@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:50:38 +0100 Subject: [PATCH 5/5] Add comment explaining x-website-token header value --- src/gofilepy/client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gofilepy/client.py b/src/gofilepy/client.py index 6fe5712..7e36718 100644 --- a/src/gofilepy/client.py +++ b/src/gofilepy/client.py @@ -264,6 +264,9 @@ class GofileClient: } headers = { # to avoid error-notPremium + # from https://gofile.io/dist/js/config.js + # appdata.wt = "4fd6sg89d7s6" + # is it constant? seems so? "x-website-token": "4fd6sg89d7s6" } return self._request(