Skip to content

Commit f1e2250

Browse files
style: ANSI art
1 parent 8f0d1ed commit f1e2250

File tree

2 files changed

+120
-2
lines changed

2 files changed

+120
-2
lines changed

src/brightdata/cli/banner.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""
2+
ANSI art banner for Bright Data Python SDK CLI.
3+
"""
4+
5+
import sys
6+
import os
7+
8+
9+
def _supports_color() -> bool:
10+
"""Check if terminal supports ANSI colors."""
11+
# Check if we're in a terminal
12+
if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty():
13+
return False
14+
15+
# Windows 10+ supports ANSI colors
16+
if sys.platform == "win32":
17+
# Check if Windows version supports ANSI
18+
try:
19+
import ctypes
20+
kernel32 = ctypes.windll.kernel32
21+
# Enable ANSI escape sequences on Windows
22+
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
23+
return True
24+
except:
25+
return False
26+
27+
# Check for common environment variables
28+
if os.getenv("TERM") in ("xterm", "xterm-256color", "screen", "screen-256color"):
29+
return True
30+
31+
return False
32+
33+
34+
def get_banner() -> str:
35+
"""
36+
Get ANSI art banner for Bright Data Python SDK.
37+
38+
Returns:
39+
Formatted banner string with colors
40+
"""
41+
banner = """
42+
43+
\033[1;33m██████╗ ██████╗ ██╗ ██████╗ ██╗ ██╗████████╗\033[0m
44+
\033[1;33m██╔══██╗██╔══██╗██║██╔════╝ ██║ ██║╚══██╔══╝\033[0m
45+
\033[1;33m██████╔╝██████╔╝██║██║ ███╗███████║ ██║ \033[0m
46+
\033[1;33m██╔══██╗██╔══██╗██║██║ ██║██╔══██║ ██║ \033[0m
47+
\033[1;33m██████╔╝██║ ██║██║╚██████╔╝██║ ██║ ██║ \033[0m
48+
\033[1;33m╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ \033[0m
49+
50+
\033[1;35m██████╗ █████╗ ████████╗ █████╗ \033[0m
51+
\033[1;35m██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗\033[0m
52+
\033[1;35m██║ ██║███████║ ██║ ███████║\033[0m
53+
\033[1;35m██║ ██║██╔══██║ ██║ ██╔══██║\033[0m
54+
\033[1;35m██████╔╝██║ ██║ ██║ ██║ ██║\033[0m
55+
\033[1;35m╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝\033[0m
56+
57+
\033[1;32m██████╗ ██╗ ██╗████████╗██╗ ██╗ ██████╗ ███╗ ██╗\033[0m
58+
\033[1;32m██╔══██╗╚██╗ ██╔╝╚══██╔══╝██║ ██║██╔═══██╗████╗ ██║\033[0m
59+
\033[1;32m██████╔╝ ╚████╔╝ ██║ ███████║██║ ██║██╔██╗ ██║\033[0m
60+
\033[1;32m██╔═══╝ ╚██╔╝ ██║ ██╔══██║██║ ██║██║╚██╗██║\033[0m
61+
\033[1;32m██║ ██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║\033[0m
62+
\033[1;32m╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝\033[0m
63+
64+
\033[1;37m███████╗██████╗ ██╗ ██╗\033[0m
65+
\033[1;37m██╔════╝██╔══██╗██║ ██╔╝\033[0m
66+
\033[1;37m███████╗██║ ██║█████╔╝ \033[0m
67+
\033[1;37m╚════██║██║ ██║██╔═██╗ \033[0m
68+
\033[1;37m███████║██████╔╝██║ ██╗\033[0m
69+
\033[1;37m╚══════╝╚═════╝ ╚═╝ ╚═╝\033[0m
70+
71+
\033[1;93m🐍\033[0m
72+
73+
"""
74+
return banner
75+
76+
77+
def print_banner() -> None:
78+
"""Print the banner to stdout with proper encoding and color support."""
79+
# Enable color support on Windows
80+
supports_color = _supports_color()
81+
82+
banner = get_banner()
83+
84+
# If no color support, strip ANSI codes
85+
if not supports_color:
86+
import re
87+
# Remove ANSI escape sequences
88+
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
89+
banner = ansi_escape.sub('', banner)
90+
91+
# Ensure UTF-8 encoding for Windows compatibility
92+
try:
93+
if hasattr(sys.stdout, 'buffer') and sys.stdout.encoding != 'utf-8':
94+
sys.stdout.buffer.write(banner.encode('utf-8'))
95+
sys.stdout.buffer.write(b'\n')
96+
else:
97+
print(banner)
98+
except (AttributeError, UnicodeEncodeError):
99+
# Fallback: print without special characters
100+
print(banner.encode('ascii', 'ignore').decode('ascii'))

src/brightdata/cli/main.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@
66

77
import click
88
import sys
9+
import io
910

1011
from .commands import scrape_group, search_group
12+
from .banner import print_banner
13+
from .utils import handle_error
1114

1215

13-
@click.group()
16+
@click.group(invoke_without_command=True)
1417
@click.version_option(version="2.0.0", prog_name="brightdata")
18+
@click.option(
19+
"--banner/--no-banner",
20+
default=True,
21+
help="Show/hide banner on startup"
22+
)
1523
@click.pass_context
16-
def cli(ctx: click.Context) -> None:
24+
def cli(ctx: click.Context, banner: bool) -> None:
1725
"""
1826
Bright Data CLI - Command-line interface for Bright Data SDK.
1927
@@ -27,6 +35,16 @@ def cli(ctx: click.Context) -> None:
2735
ctx.ensure_object(dict)
2836
# Store context for subcommands
2937
ctx.obj["api_key"] = None
38+
39+
# Show banner when invoked without subcommand and not --help/--version
40+
if ctx.invoked_subcommand is None and banner:
41+
# Check if help or version was requested
42+
import sys
43+
if "--help" not in sys.argv and "--version" not in sys.argv:
44+
print_banner()
45+
click.echo()
46+
click.echo("Run 'brightdata --help' to see available commands.")
47+
click.echo()
3048

3149

3250
# Register command groups

0 commit comments

Comments
 (0)