diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f9e1f36..794ba02 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,6 @@ repos: pytest>=7.0.0, pytest-asyncio>=0.21.0, pytest-mock>=3.10.0, - pwinput>=1.0.3, ] exclude: ^(client/|src/workato_platform/client/) diff --git a/pyproject.toml b/pyproject.toml index b7c3568..8f5e8ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,6 @@ dependencies = [ "python-dateutil>=2.8.0", "typing-extensions>=4.0.0", "packaging>=21.0", - "pwinput>=1.0.3", "cbor2>=5.7.0", "certifi>=2025.8.3", "keyring>=25.6.0", @@ -183,7 +182,6 @@ module = [ "dependency_injector.*", "asyncclick.*", "keyring.*", - "pwinput.*", ] ignore_missing_imports = true diff --git a/src/workato_platform_cli/cli/utils/config/manager.py b/src/workato_platform_cli/cli/utils/config/manager.py index e590bf5..605c154 100644 --- a/src/workato_platform_cli/cli/utils/config/manager.py +++ b/src/workato_platform_cli/cli/utils/config/manager.py @@ -10,7 +10,6 @@ import asyncclick as click import certifi import inquirer -import pwinput from workato_platform_cli import Workato from workato_platform_cli.cli.commands.projects.project_manager import ProjectManager @@ -339,18 +338,8 @@ async def _create_new_profile(self, profile_name: str) -> None: ) # Get API token - # Note: pwinput.pwinput() is called synchronously (not wrapped in - # asyncio.to_thread) because wrapping it causes terminal buffer issues - # when pasting long tokens. The async wrapper interferes with terminal - # input buffering, causing some characters to leak through before masking. - # Since this is a blocking user input operation anyway, there's no benefit - # to making it async. - # - # TODO: Once we upgrade to Python 3.14+, consider migrating to the built-in - # getpass.getpass(prompt="...", echo_char='*') which provides native masked - # input support without requiring the pwinput dependency. click.echo("🔐 Enter your API token") - token = pwinput.pwinput(prompt="Enter your Workato API token: ", mask="*") + token = await click.prompt("Enter your Workato API token", hide_input=True) if not token.strip(): click.echo("❌ No token provided") sys.exit(1) diff --git a/tests/unit/config/test_manager.py b/tests/unit/config/test_manager.py index dc3827e..b72deb4 100644 --- a/tests/unit/config/test_manager.py +++ b/tests/unit/config/test_manager.py @@ -1220,6 +1220,7 @@ async def test_setup_profile_and_project_new_flow( prompt_answers = { "Enter profile name": ["dev"], + "Enter your Workato API token": ["token-123"], "Enter project name": ["DemoProject"], } @@ -1232,16 +1233,6 @@ async def fake_prompt(message: str, **_: object) -> str: ConfigManager.__module__ + ".click.prompt", fake_prompt, ) - - # Mock pwinput for token input - def fake_pwinput(prompt: str, mask: str = "*") -> str: - assert "API token" in prompt - return "token-123" - - monkeypatch.setattr( - ConfigManager.__module__ + ".pwinput.pwinput", - fake_pwinput, - ) monkeypatch.setattr( ConfigManager.__module__ + ".click.confirm", lambda *a, **k: True, @@ -1423,6 +1414,7 @@ async def test_create_new_profile_custom_region( prompt_answers = { "Enter your custom Workato base URL": ["https://custom.workato.test"], + "Enter your Workato API token": ["custom-token"], } async def fake_prompt(message: str, **_: Any) -> str: @@ -1435,16 +1427,6 @@ async def fake_prompt(message: str, **_: Any) -> str: fake_prompt, ) - # Mock pwinput for token input - def fake_pwinput(prompt: str, mask: str = "*") -> str: - assert "API token" in prompt - return "custom-token" - - monkeypatch.setattr( - ConfigManager.__module__ + ".pwinput.pwinput", - fake_pwinput, - ) - def custom_region_prompt(questions: list[Any]) -> dict[str, str]: assert questions[0].message == "Select your Workato region" return {"region": "Custom URL"} @@ -1504,13 +1486,14 @@ async def test_create_new_profile_requires_token( lambda _questions: {"region": "US Data Center (https://www.workato.com)"}, ) - # Mock pwinput to return blank token - def fake_pwinput(prompt: str, mask: str = "*") -> str: - return " " + async def fake_prompt(message: str, **_: Any) -> str: + if "API token" in message: + return " " + return "unused" monkeypatch.setattr( - ConfigManager.__module__ + ".pwinput.pwinput", - fake_pwinput, + ConfigManager.__module__ + ".click.prompt", + fake_prompt, ) with pytest.raises(SystemExit): diff --git a/uv.lock b/uv.lock index cfe0912..a09bce5 100644 --- a/uv.lock +++ b/uv.lock @@ -1221,12 +1221,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, ] -[[package]] -name = "pwinput" -version = "1.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/c9/f6d03c2214c282251baa38c6be08f5b9f81f1ea488716cd2335e7e190b58/pwinput-1.0.3.tar.gz", hash = "sha256:ca1a8bd06e28872d751dbd4132d8637127c25b408ea3a349377314a5491426f3", size = 4433, upload-time = "2023-02-16T17:04:14.592Z" } - [[package]] name = "py-serializable" version = "2.1.0" @@ -1725,7 +1719,6 @@ dependencies = [ { name = "inquirer" }, { name = "keyring" }, { name = "packaging" }, - { name = "pwinput" }, { name = "pydantic" }, { name = "python-dateutil" }, { name = "ruff" }, @@ -1783,7 +1776,6 @@ requires-dist = [ { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0.0" }, { name = "packaging", specifier = ">=21.0" }, { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.0.0" }, - { name = "pwinput", specifier = ">=1.0.3" }, { name = "pydantic", specifier = ">=2.11.7" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=7.0.0" },