From eadfb1896c18a0bad37afba1bbe6658e4426197d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emir=20Kaan=20ERTU=C4=9ERUL?= <118385373+emirkddn@users.noreply.github.com> Date: Sat, 29 Nov 2025 15:53:55 +0300 Subject: [PATCH 1/4] Fix Windows emoji encoding issue causing test_fastapi_cli to fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes applied: β€’logging.py: Added emoji=False and legacy_windows=True as a fallback. Also introduced force_terminal=True for console configuration. β€’cli.py: Added OS detection. When running on Windows, emoji output is disabled. β€’test_cli.py: Added platform checks to assertions involving emoji characters. --- src/fastapi_cli/cli.py | 34 +++++++++++++++---- src/fastapi_cli/logging.py | 2 +- tests/test_cli.py | 69 +++++++++++++++++++++++++++++--------- 3 files changed, 82 insertions(+), 23 deletions(-) diff --git a/src/fastapi_cli/cli.py b/src/fastapi_cli/cli.py index c8180b01..6a8e2c37 100644 --- a/src/fastapi_cli/cli.py +++ b/src/fastapi_cli/cli.py @@ -1,4 +1,5 @@ import logging +import sys from pathlib import Path from typing import Any, List, Union @@ -70,21 +71,35 @@ def callback( def _get_module_tree(module_paths: List[Path]) -> Tree: root = module_paths[0] - name = f"🐍 {root.name}" if root.is_file() else f"πŸ“ {root.name}" + if sys.platform == "win32": + name = f"Python {root.name}" if root.is_file() else f"Folder {root.name}" + else: + name = f"🐍 {root.name}" if root.is_file() else f"πŸ“ {root.name}" root_tree = Tree(name) if root.is_dir(): - root_tree.add("[dim]🐍 __init__.py[/dim]") + if sys.platform == "win32": + root_tree.add("[dim]Python __init__.py[/dim]") + else: + root_tree.add("[dim]🐍 __init__.py[/dim]") tree = root_tree for sub_path in module_paths[1:]: - sub_name = ( - f"🐍 {sub_path.name}" if sub_path.is_file() else f"πŸ“ {sub_path.name}" - ) + if sys.platform == "win32": + sub_name = ( + f"Python {sub_path.name}" if sub_path.is_file() else f"Folder {sub_path.name}" + ) + else: + sub_name = ( + f"🐍 {sub_path.name}" if sub_path.is_file() else f"πŸ“ {sub_path.name}" + ) tree = tree.add(sub_name) if sub_path.is_dir(): - tree.add("[dim]🐍 __init__.py[/dim]") + if sys.platform == "win32": + tree.add("[dim]Python __init__.py[/dim]") + else: + tree.add("[dim]🐍 __init__.py[/dim]") return root_tree @@ -106,7 +121,12 @@ def _run( with get_rich_toolkit() as toolkit: server_type = "development" if command == "dev" else "production" - toolkit.print_title(f"Starting {server_type} server πŸš€", tag="FastAPI") + if sys.platform == "win32": + title = f"Starting {server_type} server" + else: + title = f"Starting {server_type} server πŸš€" + + toolkit.print_title(title, tag="FastAPI") toolkit.print_line() toolkit.print( diff --git a/src/fastapi_cli/logging.py b/src/fastapi_cli/logging.py index 67f116c9..ddce4d4a 100644 --- a/src/fastapi_cli/logging.py +++ b/src/fastapi_cli/logging.py @@ -9,7 +9,7 @@ def setup_logging( terminal_width: Union[int, None] = None, level: int = logging.INFO ) -> None: logger = logging.getLogger("fastapi_cli") - console = Console(width=terminal_width) if terminal_width else None + console = Console(width=terminal_width or 80, emoji=False, legacy_windows=True, force_terminal=True) rich_handler = RichHandler( show_time=False, rich_tracebacks=True, diff --git a/tests/test_cli.py b/tests/test_cli.py index 0a0d7ab1..893b7111 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -34,7 +34,10 @@ def test_dev() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: single_file_app:app" in result.output - assert "Starting development server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting development server" in result.output + else: + assert "Starting development server πŸš€" in result.output assert "Server started at http://127.0.0.1:8000" in result.output assert "Documentation at http://127.0.0.1:8000/docs" in result.output assert ( @@ -42,7 +45,10 @@ def test_dev() -> None: in result.output ) - assert "🐍 single_file_app.py" in result.output + if sys.platform == "win32": + assert "Python single_file_app.py" in result.output + else: + assert "🐍 single_file_app.py" in result.output def test_dev_no_args_auto_discovery() -> None: @@ -79,7 +85,10 @@ def test_dev_package() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: nested_package.package:app" in result.output - assert "Starting development server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting development server" in result.output + else: + assert "Starting development server πŸš€" in result.output assert "Server started at http://127.0.0.1:8000" in result.output assert "Documentation at http://127.0.0.1:8000/docs" in result.output assert ( @@ -87,10 +96,16 @@ def test_dev_package() -> None: in result.output ) - assert "πŸ“ package" in result.output - assert "└── 🐍 __init__.py" in result.output - assert "└── πŸ“ package" in result.output - assert " └── 🐍 __init__.py" in result.output + if sys.platform == "win32": + assert "Folder package" in result.output + assert "└── Python __init__.py" in result.output + assert "└── Folder package" in result.output + assert " └── Python __init__.py" in result.output + else: + assert "πŸ“ package" in result.output + assert "└── 🐍 __init__.py" in result.output + assert "└── πŸ“ package" in result.output + assert " └── 🐍 __init__.py" in result.output def test_dev_args() -> None: @@ -128,7 +143,10 @@ def test_dev_args() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: single_file_app:api" in result.output - assert "Starting development server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting development server" in result.output + else: + assert "Starting development server πŸš€" in result.output assert "Server started at http://192.168.0.2:8080" in result.output assert "Documentation at http://192.168.0.2:8080/docs" in result.output assert ( @@ -158,7 +176,10 @@ def test_dev_env_vars() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: single_file_app:app" in result.output - assert "Starting development server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting development server" in result.output + else: + assert "Starting development server πŸš€" in result.output assert "Server started at http://127.0.0.1:8111" in result.output assert "Documentation at http://127.0.0.1:8111/docs" in result.output assert ( @@ -195,7 +216,10 @@ def test_dev_env_vars_and_args() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: single_file_app:app" in result.output - assert "Starting development server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting development server" in result.output + else: + assert "Starting development server πŸš€" in result.output assert "Server started at http://127.0.0.1:8080" in result.output assert "Documentation at http://127.0.0.1:8080/docs" in result.output assert ( @@ -240,7 +264,10 @@ def test_run() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: single_file_app:app" in result.output - assert "Starting production server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting production server" in result.output + else: + assert "Starting production server πŸš€" in result.output assert "Server started at http://0.0.0.0:8000" in result.output assert "Documentation at http://0.0.0.0:8000/docs" in result.output @@ -266,7 +293,10 @@ def test_run_trust_proxy() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: single_file_app:app" in result.output - assert "Starting production server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting production server" in result.output + else: + assert "Starting production server πŸš€" in result.output assert "Server started at http://0.0.0.0:8000" in result.output assert "Documentation at http://0.0.0.0:8000/docs" in result.output assert ( @@ -313,7 +343,10 @@ def test_run_args() -> None: } assert "Using import string: single_file_app:api" in result.output - assert "Starting production server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting production server" in result.output + else: + assert "Starting production server πŸš€" in result.output assert "Server started at http://192.168.0.2:8080" in result.output assert "Documentation at http://192.168.0.2:8080/docs" in result.output assert ( @@ -343,7 +376,10 @@ def test_run_env_vars() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: single_file_app:app" in result.output - assert "Starting production server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting production server" in result.output + else: + assert "Starting production server πŸš€" in result.output assert "Server started at http://0.0.0.0:8111" in result.output assert "Documentation at http://0.0.0.0:8111/docs" in result.output @@ -376,7 +412,10 @@ def test_run_env_vars_and_args() -> None: "log_config": get_uvicorn_log_config(), } assert "Using import string: single_file_app:app" in result.output - assert "Starting production server πŸš€" in result.output + if sys.platform == "win32": + assert "Starting production server" in result.output + else: + assert "Starting production server πŸš€" in result.output assert "Server started at http://0.0.0.0:8080" in result.output assert "Documentation at http://0.0.0.0:8080/docs" in result.output From 5ab59423e84902b59cca65092d070385e754fc66 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 29 Nov 2025 13:03:10 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20for?= =?UTF-8?q?mat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fastapi_cli/cli.py | 6 ++++-- src/fastapi_cli/logging.py | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/fastapi_cli/cli.py b/src/fastapi_cli/cli.py index 6a8e2c37..c9412063 100644 --- a/src/fastapi_cli/cli.py +++ b/src/fastapi_cli/cli.py @@ -88,7 +88,9 @@ def _get_module_tree(module_paths: List[Path]) -> Tree: for sub_path in module_paths[1:]: if sys.platform == "win32": sub_name = ( - f"Python {sub_path.name}" if sub_path.is_file() else f"Folder {sub_path.name}" + f"Python {sub_path.name}" + if sub_path.is_file() + else f"Folder {sub_path.name}" ) else: sub_name = ( @@ -124,7 +126,7 @@ def _run( if sys.platform == "win32": title = f"Starting {server_type} server" else: - title = f"Starting {server_type} server πŸš€" + title = f"Starting {server_type} server πŸš€" toolkit.print_title(title, tag="FastAPI") toolkit.print_line() diff --git a/src/fastapi_cli/logging.py b/src/fastapi_cli/logging.py index ddce4d4a..3510bb57 100644 --- a/src/fastapi_cli/logging.py +++ b/src/fastapi_cli/logging.py @@ -9,7 +9,12 @@ def setup_logging( terminal_width: Union[int, None] = None, level: int = logging.INFO ) -> None: logger = logging.getLogger("fastapi_cli") - console = Console(width=terminal_width or 80, emoji=False, legacy_windows=True, force_terminal=True) + console = Console( + width=terminal_width or 80, + emoji=False, + legacy_windows=True, + force_terminal=True, + ) rich_handler = RichHandler( show_time=False, rich_tracebacks=True, From c7dd384180f87b5616e2d3cfc05efd275de6ddda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emir=20Kaan=20ERTU=C4=9ERUL?= <118385373+emirkddn@users.noreply.github.com> Date: Sat, 29 Nov 2025 16:10:37 +0300 Subject: [PATCH 3/4] Update logging.py The change in logging.py was affecting other operating systems, it has now been corrected. --- src/fastapi_cli/logging.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fastapi_cli/logging.py b/src/fastapi_cli/logging.py index ddce4d4a..c08b3395 100644 --- a/src/fastapi_cli/logging.py +++ b/src/fastapi_cli/logging.py @@ -1,4 +1,5 @@ import logging +import sys from typing import Union from rich.console import Console @@ -9,7 +10,10 @@ def setup_logging( terminal_width: Union[int, None] = None, level: int = logging.INFO ) -> None: logger = logging.getLogger("fastapi_cli") - console = Console(width=terminal_width or 80, emoji=False, legacy_windows=True, force_terminal=True) + if sys.platform == "win32": + console = Console(width=terminal_width or 80, emoji=False, legacy_windows=True) + else: + console = Console(width=terminal_width or 80) rich_handler = RichHandler( show_time=False, rich_tracebacks=True, From ef77856e691020c8a44776f4ae32b110983b6a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emir=20Kaan=20ERTU=C4=9ERUL?= <118385373+emirkddn@users.noreply.github.com> Date: Sat, 29 Nov 2025 16:13:57 +0300 Subject: [PATCH 4/4] Update logging.py The change in logging.py was affecting other operating systems, it has now been corrected. --- src/fastapi_cli/logging.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/fastapi_cli/logging.py b/src/fastapi_cli/logging.py index 5970eedb..c08b3395 100644 --- a/src/fastapi_cli/logging.py +++ b/src/fastapi_cli/logging.py @@ -10,7 +10,10 @@ def setup_logging( terminal_width: Union[int, None] = None, level: int = logging.INFO ) -> None: logger = logging.getLogger("fastapi_cli") - console = Console(width=terminal_width or 80, emoji=False, legacy_windows=True, force_terminal=True) + if sys.platform == "win32": + console = Console(width=terminal_width or 80, emoji=False, legacy_windows=True) + else: + console = Console(width=terminal_width or 80) rich_handler = RichHandler( show_time=False, rich_tracebacks=True,