textual-universal-directorytree


Nametextual-universal-directorytree JSON
Version 1.5.0 PyPI version JSON
download
home_pageNone
SummaryA Textual Directory Tree for all File Systems
upload_time2024-03-20 17:46:42
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords fsspec plugin textual tui
VCS
bugtrack_url
requirements adlfs aiobotocore aiohttp aioitertools aiosignal attrs azure-core azure-datalake-store azure-identity azure-storage-blob bcrypt botocore cachetools certifi cffi charset-normalizer cryptography decorator frozenlist fsspec gcsfs google-api-core google-auth google-auth-oauthlib google-cloud-core google-cloud-storage google-crc32c google-resumable-media googleapis-common-protos idna isodate jmespath linkify-it-py markdown-it-py mdit-py-plugins mdurl msal msal-extensions multidict oauthlib packaging paramiko portalocker protobuf pyasn1 pyasn1-modules pycparser pygments pyjwt pynacl python-dateutil requests requests-oauthlib rich rsa s3fs six textual typing-extensions uc-micro-py universal-pathlib urllib3 wrapt yarl
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <h1 align="center">textual-universal-directorytree</h1>

<div align="center">
  <a href="https://github.com/juftin/textual-universal-directorytree">
    <img src="docs/screenshots/test_github_screenshot.svg" alt="textual-universal-directorytree" />
  </a>
</div>

<p align="center">
<a href="https://textual.textualize.io/widgets/directory_tree/">DirectoryTree</a> widget for <a href="https://textual.textualize.io/">textual</a>, compatible with all filesystems
</p>

<p align="center">
  <a href="https://github.com/juftin/textual-universal-directorytree"><img src="https://img.shields.io/pypi/v/textual-universal-directorytree?color=blue&label=%F0%9F%93%81%20textual-universal-directorytree" alt="PyPI"></a>
  <a href="https://pypi.python.org/pypi/textual-universal-directorytree/"><img src="https://img.shields.io/pypi/pyversions/textual-universal-directorytree" alt="PyPI - Python Version"></a>
  <a href="https://github.com/juftin/textual-universal-directorytree/blob/main/LICENSE"><img src="https://img.shields.io/github/license/juftin/textual-universal-directorytree?color=blue&label=License" alt="GitHub License"></a>
  <a href="https://juftin.github.io/textual-universal-directorytree/"><img src="https://img.shields.io/static/v1?message=docs&color=526CFE&logo=Material+for+MkDocs&logoColor=FFFFFF&label=" alt="docs"></a>
  <a href="https://github.com/juftin/textual-universal-directorytree/actions/workflows/tests.yaml?query=branch%3Amain"><img src="https://github.com/juftin/textual-universal-directorytree/actions/workflows/tests.yaml/badge.svg?branch=main" alt="Testing Status"></a>
  <a href="https://github.com/pypa/hatch"><img src="https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg" alt="Hatch project"></a>
  <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
  <a href="https://github.com/pre-commit/pre-commit"><img src="https://img.shields.io/badge/pre--commit-enabled-lightgreen?logo=pre-commit" alt="pre-commit"></a>
  <a href="https://github.com/semantic-release/semantic-release"><img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="semantic-release"></a>
  <a href="https://gitmoji.dev"><img src="https://img.shields.io/badge/gitmoji-%20😜%20😍-FFDD67.svg" alt="Gitmoji"></a>
</p>

## Installation

```shell
pip install textual-universal-directorytree
```

### Extra Dependencies

Some filesystems require additional dependencies to be installed.
The `remote` extra includes all the known optional dependencies. See the
[Filesystems Supported](#filesystems-supported) section for more information.

```shell
pip install "textual-universal-directorytree[remote]"
```

## Usage

The below example shows how to use `textual-universal-directorytree` in a Textual app.
It uses the GitHub filesystem to display the contents of the textual GitHub repository.
It requires the `requests` library to be installed (or the `remote` extra).

```python
from __future__ import annotations

from typing import Any, ClassVar

from rich.syntax import Syntax
from textual import on
from textual.app import App, ComposeResult
from textual.binding import BindingType
from textual.containers import Horizontal, VerticalScroll
from textual.widgets import DirectoryTree, Footer, Header, Static

from textual_universal_directorytree import UniversalDirectoryTree, UPath


class UniversalDirectoryTreeApp(App):
    """
    The power of upath and fsspec in a Textual app
    """

    TITLE = "UniversalDirectoryTree"

    CSS = """
    UniversalDirectoryTree {
        max-width: 50%;
        width: auto;
        height: 100%;
        dock: left;
    }
    """

    BINDINGS: ClassVar[list[BindingType]] = [
        ("q", "quit", "Quit"),
    ]

    def __init__(self, path: str | UPath, *args: Any, **kwargs: Any):
        super().__init__(*args, **kwargs)
        self.universal_path = UPath(path).resolve()
        self.directory_tree = UniversalDirectoryTree(path=self.universal_path)
        self.file_content = Static(expand=True)

    def compose(self) -> ComposeResult:
        yield Header()
        yield Horizontal(self.directory_tree, VerticalScroll(self.file_content))
        yield Footer()

    @on(DirectoryTree.FileSelected)
    def handle_file_selected(self, message: DirectoryTree.FileSelected) -> None:
        """
        Do something with the selected file.

        Objects returned by the FileSelected event are upath.UPath objects and
        they are compatible with the familiar pathlib.Path API built into Python.
        """
        self.sub_title = str(message.path)
        try:
            file_content = message.path.read_text()
        except UnicodeDecodeError:
            self.file_content.update("")
            return None
        lexer = Syntax.guess_lexer(path=message.path.name, code=file_content)
        code = Syntax(code=file_content, lexer=lexer)
        self.file_content.update(code)
```

Run the above app in your terminal:

```python
python -m textual_universal_directorytree github://juftin:textual-universal-directorytree@main/
```

## Filesystems Supported

`textual-universal-directorytree` leverages [fsspec](https://github.com/fsspec/filesystem_spec) and
[universal_pathlib](https://github.com/fsspec/universal_pathlib) to enable compatibility with
local and remote filesystems.

In some cases you need to install a filesystem-specific library
to enable compatibility with that filesystem. For example, to enable compatibility with AWS S3 you must
install [s3fs](https://github.com/fsspec/s3fs) which is an `fsspec` implementation for S3.

The following filesystems are known to be supported by `textual-universal-directorytree`, but it's possible
that [others filesystems](https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations)
are supported as well and just haven't been tested. If you find a filesystem that works, please open an issue.

| File System     | Format                            | Optional Dependencies                            |
| --------------- | --------------------------------- | ------------------------------------------------ |
| Local           | `path/to/file`                    | None                                             |
| Local           | `file://path/to/file`             | None                                             |
| AWS S3          | `s3://bucket/path`                | [s3fs](https://github.com/fsspec/s3fs)           |
| AWS S3          | `s3a://bucket/path`               | [s3fs](https://github.com/fsspec/s3fs)           |
| Google GCS      | `gs://bucket/path`                | [gcsfs](https://github.com/fsspec/gcsfs)         |
| Azure Data Lake | `adl://bucket/path`               | [adlfs](https://github.com/fsspec/adlfs)         |
| Azure Blob      | `abfs://bucket/path`              | [adlfs](https://github.com/fsspec/adlfs)         |
| Azure Blob      | `az://bucket/path`                | [adlfs](https://github.com/fsspec/adlfs)         |
| GitHub          | `github://owner:repo@branch`      | [requests](https://github.com/requests/requests) |
| GitHub          | `github://owner:repo@branch/path` | [requests](https://github.com/requests/requests) |
| SSH             | `ssh://user@host:port/path`       | [paramiko](https://github.com/paramiko/paramiko) |
| SFTP            | `sftp://user@host:port/path`      | [paramiko](https://github.com/paramiko/paramiko) |

## License

`textual-universal-directorytree` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html)
license.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "textual-universal-directorytree",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "fsspec, plugin, textual, tui",
    "author": null,
    "author_email": "Justin Flannery <juftin@juftin.com>",
    "download_url": "https://files.pythonhosted.org/packages/5d/a5/1e0f4d69c3e519fd39dcc2567cae26bcc43ab377e9701cd89a1def549647/textual_universal_directorytree-1.5.0.tar.gz",
    "platform": null,
    "description": "<h1 align=\"center\">textual-universal-directorytree</h1>\n\n<div align=\"center\">\n  <a href=\"https://github.com/juftin/textual-universal-directorytree\">\n    <img src=\"docs/screenshots/test_github_screenshot.svg\" alt=\"textual-universal-directorytree\" />\n  </a>\n</div>\n\n<p align=\"center\">\n<a href=\"https://textual.textualize.io/widgets/directory_tree/\">DirectoryTree</a> widget for <a href=\"https://textual.textualize.io/\">textual</a>, compatible with all filesystems\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/juftin/textual-universal-directorytree\"><img src=\"https://img.shields.io/pypi/v/textual-universal-directorytree?color=blue&label=%F0%9F%93%81%20textual-universal-directorytree\" alt=\"PyPI\"></a>\n  <a href=\"https://pypi.python.org/pypi/textual-universal-directorytree/\"><img src=\"https://img.shields.io/pypi/pyversions/textual-universal-directorytree\" alt=\"PyPI - Python Version\"></a>\n  <a href=\"https://github.com/juftin/textual-universal-directorytree/blob/main/LICENSE\"><img src=\"https://img.shields.io/github/license/juftin/textual-universal-directorytree?color=blue&label=License\" alt=\"GitHub License\"></a>\n  <a href=\"https://juftin.github.io/textual-universal-directorytree/\"><img src=\"https://img.shields.io/static/v1?message=docs&color=526CFE&logo=Material+for+MkDocs&logoColor=FFFFFF&label=\" alt=\"docs\"></a>\n  <a href=\"https://github.com/juftin/textual-universal-directorytree/actions/workflows/tests.yaml?query=branch%3Amain\"><img src=\"https://github.com/juftin/textual-universal-directorytree/actions/workflows/tests.yaml/badge.svg?branch=main\" alt=\"Testing Status\"></a>\n  <a href=\"https://github.com/pypa/hatch\"><img src=\"https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg\" alt=\"Hatch project\"></a>\n  <a href=\"https://github.com/astral-sh/ruff\"><img src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json\" alt=\"Ruff\"></a>\n  <a href=\"https://github.com/pre-commit/pre-commit\"><img src=\"https://img.shields.io/badge/pre--commit-enabled-lightgreen?logo=pre-commit\" alt=\"pre-commit\"></a>\n  <a href=\"https://github.com/semantic-release/semantic-release\"><img src=\"https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg\" alt=\"semantic-release\"></a>\n  <a href=\"https://gitmoji.dev\"><img src=\"https://img.shields.io/badge/gitmoji-%20\ud83d\ude1c%20\ud83d\ude0d-FFDD67.svg\" alt=\"Gitmoji\"></a>\n</p>\n\n## Installation\n\n```shell\npip install textual-universal-directorytree\n```\n\n### Extra Dependencies\n\nSome filesystems require additional dependencies to be installed.\nThe `remote` extra includes all the known optional dependencies. See the\n[Filesystems Supported](#filesystems-supported) section for more information.\n\n```shell\npip install \"textual-universal-directorytree[remote]\"\n```\n\n## Usage\n\nThe below example shows how to use `textual-universal-directorytree` in a Textual app.\nIt uses the GitHub filesystem to display the contents of the textual GitHub repository.\nIt requires the `requests` library to be installed (or the `remote` extra).\n\n```python\nfrom __future__ import annotations\n\nfrom typing import Any, ClassVar\n\nfrom rich.syntax import Syntax\nfrom textual import on\nfrom textual.app import App, ComposeResult\nfrom textual.binding import BindingType\nfrom textual.containers import Horizontal, VerticalScroll\nfrom textual.widgets import DirectoryTree, Footer, Header, Static\n\nfrom textual_universal_directorytree import UniversalDirectoryTree, UPath\n\n\nclass UniversalDirectoryTreeApp(App):\n    \"\"\"\n    The power of upath and fsspec in a Textual app\n    \"\"\"\n\n    TITLE = \"UniversalDirectoryTree\"\n\n    CSS = \"\"\"\n    UniversalDirectoryTree {\n        max-width: 50%;\n        width: auto;\n        height: 100%;\n        dock: left;\n    }\n    \"\"\"\n\n    BINDINGS: ClassVar[list[BindingType]] = [\n        (\"q\", \"quit\", \"Quit\"),\n    ]\n\n    def __init__(self, path: str | UPath, *args: Any, **kwargs: Any):\n        super().__init__(*args, **kwargs)\n        self.universal_path = UPath(path).resolve()\n        self.directory_tree = UniversalDirectoryTree(path=self.universal_path)\n        self.file_content = Static(expand=True)\n\n    def compose(self) -> ComposeResult:\n        yield Header()\n        yield Horizontal(self.directory_tree, VerticalScroll(self.file_content))\n        yield Footer()\n\n    @on(DirectoryTree.FileSelected)\n    def handle_file_selected(self, message: DirectoryTree.FileSelected) -> None:\n        \"\"\"\n        Do something with the selected file.\n\n        Objects returned by the FileSelected event are upath.UPath objects and\n        they are compatible with the familiar pathlib.Path API built into Python.\n        \"\"\"\n        self.sub_title = str(message.path)\n        try:\n            file_content = message.path.read_text()\n        except UnicodeDecodeError:\n            self.file_content.update(\"\")\n            return None\n        lexer = Syntax.guess_lexer(path=message.path.name, code=file_content)\n        code = Syntax(code=file_content, lexer=lexer)\n        self.file_content.update(code)\n```\n\nRun the above app in your terminal:\n\n```python\npython -m textual_universal_directorytree github://juftin:textual-universal-directorytree@main/\n```\n\n## Filesystems Supported\n\n`textual-universal-directorytree` leverages [fsspec](https://github.com/fsspec/filesystem_spec) and\n[universal_pathlib](https://github.com/fsspec/universal_pathlib) to enable compatibility with\nlocal and remote filesystems.\n\nIn some cases you need to install a filesystem-specific library\nto enable compatibility with that filesystem. For example, to enable compatibility with AWS S3 you must\ninstall [s3fs](https://github.com/fsspec/s3fs) which is an `fsspec` implementation for S3.\n\nThe following filesystems are known to be supported by `textual-universal-directorytree`, but it's possible\nthat [others filesystems](https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations)\nare supported as well and just haven't been tested. If you find a filesystem that works, please open an issue.\n\n| File System     | Format                            | Optional Dependencies                            |\n| --------------- | --------------------------------- | ------------------------------------------------ |\n| Local           | `path/to/file`                    | None                                             |\n| Local           | `file://path/to/file`             | None                                             |\n| AWS S3          | `s3://bucket/path`                | [s3fs](https://github.com/fsspec/s3fs)           |\n| AWS S3          | `s3a://bucket/path`               | [s3fs](https://github.com/fsspec/s3fs)           |\n| Google GCS      | `gs://bucket/path`                | [gcsfs](https://github.com/fsspec/gcsfs)         |\n| Azure Data Lake | `adl://bucket/path`               | [adlfs](https://github.com/fsspec/adlfs)         |\n| Azure Blob      | `abfs://bucket/path`              | [adlfs](https://github.com/fsspec/adlfs)         |\n| Azure Blob      | `az://bucket/path`                | [adlfs](https://github.com/fsspec/adlfs)         |\n| GitHub          | `github://owner:repo@branch`      | [requests](https://github.com/requests/requests) |\n| GitHub          | `github://owner:repo@branch/path` | [requests](https://github.com/requests/requests) |\n| SSH             | `ssh://user@host:port/path`       | [paramiko](https://github.com/paramiko/paramiko) |\n| SFTP            | `sftp://user@host:port/path`      | [paramiko](https://github.com/paramiko/paramiko) |\n\n## License\n\n`textual-universal-directorytree` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html)\nlicense.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A Textual Directory Tree for all File Systems",
    "version": "1.5.0",
    "project_urls": {
        "Documentation": "https://github.com/juftin/textual-universal-directorytree#readme",
        "Issues": "https://github.com/juftin/textual-universal-directorytree/issues",
        "Source": "https://github.com/juftin/textual-universal-directorytree"
    },
    "split_keywords": [
        "fsspec",
        " plugin",
        " textual",
        " tui"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "cce3729bb0851b4e44948e05be53ba29d9f69dda874b1e4ef19cc28063a8ab88",
                "md5": "d96580122e1e1982381a7115cf7951d6",
                "sha256": "f36ad044d91f364b41b8b17decfb262326e7173ae6c29fd23c55ccf12bb047b7"
            },
            "downloads": -1,
            "filename": "textual_universal_directorytree-1.5.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d96580122e1e1982381a7115cf7951d6",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 9749,
            "upload_time": "2024-03-20T17:46:41",
            "upload_time_iso_8601": "2024-03-20T17:46:41.463587Z",
            "url": "https://files.pythonhosted.org/packages/cc/e3/729bb0851b4e44948e05be53ba29d9f69dda874b1e4ef19cc28063a8ab88/textual_universal_directorytree-1.5.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5da51e0f4d69c3e519fd39dcc2567cae26bcc43ab377e9701cd89a1def549647",
                "md5": "faa22087c5aed0536b532c6b9ecde78f",
                "sha256": "8a6bb6c79d6fdd3bf321eb30156f935d7ceb49024692ff519583fde0a500b05f"
            },
            "downloads": -1,
            "filename": "textual_universal_directorytree-1.5.0.tar.gz",
            "has_sig": false,
            "md5_digest": "faa22087c5aed0536b532c6b9ecde78f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 121348,
            "upload_time": "2024-03-20T17:46:42",
            "upload_time_iso_8601": "2024-03-20T17:46:42.647485Z",
            "url": "https://files.pythonhosted.org/packages/5d/a5/1e0f4d69c3e519fd39dcc2567cae26bcc43ab377e9701cd89a1def549647/textual_universal_directorytree-1.5.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-20 17:46:42",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "juftin",
    "github_project": "textual-universal-directorytree#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "adlfs",
            "specs": [
                [
                    "==",
                    "2024.2.0"
                ]
            ]
        },
        {
            "name": "aiobotocore",
            "specs": [
                [
                    "==",
                    "2.12.1"
                ]
            ]
        },
        {
            "name": "aiohttp",
            "specs": [
                [
                    "==",
                    "3.9.3"
                ]
            ]
        },
        {
            "name": "aioitertools",
            "specs": [
                [
                    "==",
                    "0.11.0"
                ]
            ]
        },
        {
            "name": "aiosignal",
            "specs": [
                [
                    "==",
                    "1.3.1"
                ]
            ]
        },
        {
            "name": "attrs",
            "specs": [
                [
                    "==",
                    "23.2.0"
                ]
            ]
        },
        {
            "name": "azure-core",
            "specs": [
                [
                    "==",
                    "1.30.1"
                ]
            ]
        },
        {
            "name": "azure-datalake-store",
            "specs": [
                [
                    "==",
                    "0.0.53"
                ]
            ]
        },
        {
            "name": "azure-identity",
            "specs": [
                [
                    "==",
                    "1.15.0"
                ]
            ]
        },
        {
            "name": "azure-storage-blob",
            "specs": [
                [
                    "==",
                    "12.19.1"
                ]
            ]
        },
        {
            "name": "bcrypt",
            "specs": [
                [
                    "==",
                    "4.1.2"
                ]
            ]
        },
        {
            "name": "botocore",
            "specs": [
                [
                    "==",
                    "1.34.51"
                ]
            ]
        },
        {
            "name": "cachetools",
            "specs": [
                [
                    "==",
                    "5.3.3"
                ]
            ]
        },
        {
            "name": "certifi",
            "specs": [
                [
                    "==",
                    "2024.2.2"
                ]
            ]
        },
        {
            "name": "cffi",
            "specs": [
                [
                    "==",
                    "1.16.0"
                ]
            ]
        },
        {
            "name": "charset-normalizer",
            "specs": [
                [
                    "==",
                    "3.3.2"
                ]
            ]
        },
        {
            "name": "cryptography",
            "specs": [
                [
                    "==",
                    "42.0.5"
                ]
            ]
        },
        {
            "name": "decorator",
            "specs": [
                [
                    "==",
                    "5.1.1"
                ]
            ]
        },
        {
            "name": "frozenlist",
            "specs": [
                [
                    "==",
                    "1.4.1"
                ]
            ]
        },
        {
            "name": "fsspec",
            "specs": [
                [
                    "==",
                    "2024.3.1"
                ]
            ]
        },
        {
            "name": "gcsfs",
            "specs": [
                [
                    "==",
                    "2024.3.1"
                ]
            ]
        },
        {
            "name": "google-api-core",
            "specs": [
                [
                    "==",
                    "2.17.1"
                ]
            ]
        },
        {
            "name": "google-auth",
            "specs": [
                [
                    "==",
                    "2.29.0"
                ]
            ]
        },
        {
            "name": "google-auth-oauthlib",
            "specs": [
                [
                    "==",
                    "1.2.0"
                ]
            ]
        },
        {
            "name": "google-cloud-core",
            "specs": [
                [
                    "==",
                    "2.4.1"
                ]
            ]
        },
        {
            "name": "google-cloud-storage",
            "specs": [
                [
                    "==",
                    "2.16.0"
                ]
            ]
        },
        {
            "name": "google-crc32c",
            "specs": [
                [
                    "==",
                    "1.5.0"
                ]
            ]
        },
        {
            "name": "google-resumable-media",
            "specs": [
                [
                    "==",
                    "2.7.0"
                ]
            ]
        },
        {
            "name": "googleapis-common-protos",
            "specs": [
                [
                    "==",
                    "1.63.0"
                ]
            ]
        },
        {
            "name": "idna",
            "specs": [
                [
                    "==",
                    "3.6"
                ]
            ]
        },
        {
            "name": "isodate",
            "specs": [
                [
                    "==",
                    "0.6.1"
                ]
            ]
        },
        {
            "name": "jmespath",
            "specs": [
                [
                    "==",
                    "1.0.1"
                ]
            ]
        },
        {
            "name": "linkify-it-py",
            "specs": [
                [
                    "==",
                    "2.0.3"
                ]
            ]
        },
        {
            "name": "markdown-it-py",
            "specs": [
                [
                    "==",
                    "3.0.0"
                ]
            ]
        },
        {
            "name": "mdit-py-plugins",
            "specs": [
                [
                    "==",
                    "0.4.0"
                ]
            ]
        },
        {
            "name": "mdurl",
            "specs": [
                [
                    "==",
                    "0.1.2"
                ]
            ]
        },
        {
            "name": "msal",
            "specs": [
                [
                    "==",
                    "1.28.0"
                ]
            ]
        },
        {
            "name": "msal-extensions",
            "specs": [
                [
                    "==",
                    "1.1.0"
                ]
            ]
        },
        {
            "name": "multidict",
            "specs": [
                [
                    "==",
                    "6.0.5"
                ]
            ]
        },
        {
            "name": "oauthlib",
            "specs": [
                [
                    "==",
                    "3.2.2"
                ]
            ]
        },
        {
            "name": "packaging",
            "specs": [
                [
                    "==",
                    "24.0"
                ]
            ]
        },
        {
            "name": "paramiko",
            "specs": [
                [
                    "==",
                    "3.4.0"
                ]
            ]
        },
        {
            "name": "portalocker",
            "specs": [
                [
                    "==",
                    "2.8.2"
                ]
            ]
        },
        {
            "name": "protobuf",
            "specs": [
                [
                    "==",
                    "4.25.3"
                ]
            ]
        },
        {
            "name": "pyasn1",
            "specs": [
                [
                    "==",
                    "0.5.1"
                ]
            ]
        },
        {
            "name": "pyasn1-modules",
            "specs": [
                [
                    "==",
                    "0.3.0"
                ]
            ]
        },
        {
            "name": "pycparser",
            "specs": [
                [
                    "==",
                    "2.21"
                ]
            ]
        },
        {
            "name": "pygments",
            "specs": [
                [
                    "==",
                    "2.17.2"
                ]
            ]
        },
        {
            "name": "pyjwt",
            "specs": [
                [
                    "==",
                    "2.8.0"
                ]
            ]
        },
        {
            "name": "pynacl",
            "specs": [
                [
                    "==",
                    "1.5.0"
                ]
            ]
        },
        {
            "name": "python-dateutil",
            "specs": [
                [
                    "==",
                    "2.9.0.post0"
                ]
            ]
        },
        {
            "name": "requests",
            "specs": [
                [
                    "==",
                    "2.31.0"
                ]
            ]
        },
        {
            "name": "requests-oauthlib",
            "specs": [
                [
                    "==",
                    "1.4.0"
                ]
            ]
        },
        {
            "name": "rich",
            "specs": [
                [
                    "==",
                    "13.7.1"
                ]
            ]
        },
        {
            "name": "rsa",
            "specs": [
                [
                    "==",
                    "4.9"
                ]
            ]
        },
        {
            "name": "s3fs",
            "specs": [
                [
                    "==",
                    "2024.3.1"
                ]
            ]
        },
        {
            "name": "six",
            "specs": [
                [
                    "==",
                    "1.16.0"
                ]
            ]
        },
        {
            "name": "textual",
            "specs": [
                [
                    "==",
                    "0.53.1"
                ]
            ]
        },
        {
            "name": "typing-extensions",
            "specs": [
                [
                    "==",
                    "4.10.0"
                ]
            ]
        },
        {
            "name": "uc-micro-py",
            "specs": [
                [
                    "==",
                    "1.0.3"
                ]
            ]
        },
        {
            "name": "universal-pathlib",
            "specs": [
                [
                    "==",
                    "0.2.2"
                ]
            ]
        },
        {
            "name": "urllib3",
            "specs": [
                [
                    "==",
                    "2.0.7"
                ]
            ]
        },
        {
            "name": "wrapt",
            "specs": [
                [
                    "==",
                    "1.16.0"
                ]
            ]
        },
        {
            "name": "yarl",
            "specs": [
                [
                    "==",
                    "1.9.4"
                ]
            ]
        }
    ],
    "lcname": "textual-universal-directorytree"
}
        
Elapsed time: 8.53218s