Coverage for typer/main.py: 100%

491 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-04-14 00:18 +0000

1import inspect 1iceafgbdh

2import os 1iceafgbdh

3import platform 1iceafgbdh

4import shutil 1iceafgbdh

5import subprocess 1iceafgbdh

6import sys 1iceafgbdh

7import traceback 1iceafgbdh

8from datetime import datetime 1iceafgbdh

9from enum import Enum 1iceafgbdh

10from functools import update_wrapper 1iceafgbdh

11from pathlib import Path 1iceafgbdh

12from traceback import FrameSummary, StackSummary 1iceafgbdh

13from types import TracebackType 1iceafgbdh

14from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union 1iceafgbdh

15from uuid import UUID 1iceafgbdh

16 

17import click 1iceafgbdh

18 

19from ._typing import get_args, get_origin, is_union 1iceafgbdh

20from .completion import get_completion_inspect_parameters 1iceafgbdh

21from .core import ( 1iceafgbdh

22 DEFAULT_MARKUP_MODE, 

23 MarkupMode, 

24 TyperArgument, 

25 TyperCommand, 

26 TyperGroup, 

27 TyperOption, 

28) 

29from .models import ( 1iceafgbdh

30 AnyType, 

31 ArgumentInfo, 

32 CommandFunctionType, 

33 CommandInfo, 

34 Default, 

35 DefaultPlaceholder, 

36 DeveloperExceptionConfig, 

37 FileBinaryRead, 

38 FileBinaryWrite, 

39 FileText, 

40 FileTextWrite, 

41 NoneType, 

42 OptionInfo, 

43 ParameterInfo, 

44 ParamMeta, 

45 Required, 

46 TyperInfo, 

47 TyperPath, 

48) 

49from .utils import get_params_from_function 1iceafgbdh

50 

51try: 1iceafgbdh

52 import rich 1iceafgbdh

53 from rich.traceback import Traceback 1iceafgbdh

54 

55 from . import rich_utils 1iceafgbdh

56 

57 console_stderr = rich_utils._get_rich_console(stderr=True) 1iceafgbdh

58 

59except ImportError: # pragma: no cover 

60 rich = None # type: ignore 

61 

62_original_except_hook = sys.excepthook 1iceafgbdh

63_typer_developer_exception_attr_name = "__typer_developer_exception__" 1iceafgbdh

64 

65 

66def except_hook( 1ceafgbdh

67 exc_type: Type[BaseException], exc_value: BaseException, tb: Optional[TracebackType] 

68) -> None: 

69 exception_config: Union[DeveloperExceptionConfig, None] = getattr( 1iceafgbdh

70 exc_value, _typer_developer_exception_attr_name, None 

71 ) 

72 standard_traceback = os.getenv("_TYPER_STANDARD_TRACEBACK") 1iceafgbdh

73 if ( 1cabd

74 standard_traceback 

75 or not exception_config 

76 or not exception_config.pretty_exceptions_enable 

77 ): 

78 _original_except_hook(exc_type, exc_value, tb) 1iceafgbdh

79 return 1iceafgbdh

80 typer_path = os.path.dirname(__file__) 1iceafgbdh

81 click_path = os.path.dirname(click.__file__) 1iceafgbdh

82 supress_internal_dir_names = [typer_path, click_path] 1iceafgbdh

83 exc = exc_value 1iceafgbdh

84 if rich: 1iceafgbdh

85 from .rich_utils import MAX_WIDTH 1iceafgbdh

86 

87 rich_tb = Traceback.from_exception( 1iceafgbdh

88 type(exc), 

89 exc, 

90 exc.__traceback__, 

91 show_locals=exception_config.pretty_exceptions_show_locals, 

92 suppress=supress_internal_dir_names, 

93 width=MAX_WIDTH, 

94 ) 

95 console_stderr.print(rich_tb) 1iceafgbdh

96 return 1iceafgbdh

97 tb_exc = traceback.TracebackException.from_exception(exc) 1iceafgbdh

98 stack: List[FrameSummary] = [] 1iceafgbdh

99 for frame in tb_exc.stack: 1iceafgbdh

100 if any(frame.filename.startswith(path) for path in supress_internal_dir_names): 1iceafgbdh

101 if not exception_config.pretty_exceptions_short: 1iceafgbdh

102 # Hide the line for internal libraries, Typer and Click 

103 stack.append( 1iceafgbdh

104 traceback.FrameSummary( 

105 filename=frame.filename, 

106 lineno=frame.lineno, 

107 name=frame.name, 

108 line="", 

109 ) 

110 ) 

111 else: 

112 stack.append(frame) 1iceafgbdh

113 # Type ignore ref: https://github.com/python/typeshed/pull/8244 

114 final_stack_summary = StackSummary.from_list(stack) 1iceafgbdh

115 tb_exc.stack = final_stack_summary 1iceafgbdh

116 for line in tb_exc.format(): 1iceafgbdh

117 print(line, file=sys.stderr) 1iceafgbdh

118 return 1iceafgbdh

119 

120 

121def get_install_completion_arguments() -> Tuple[click.Parameter, click.Parameter]: 1iceafgbdh

122 install_param, show_param = get_completion_inspect_parameters() 1iceafgbdh

123 click_install_param, _ = get_click_param(install_param) 1iceafgbdh

124 click_show_param, _ = get_click_param(show_param) 1iceafgbdh

125 return click_install_param, click_show_param 1iceafgbdh

126 

127 

128class Typer: 1iceafgbdh

129 def __init__( 1ceafgbdh

130 self, 

131 *, 

132 name: Optional[str] = Default(None), 

133 cls: Optional[Type[TyperGroup]] = Default(None), 

134 invoke_without_command: bool = Default(False), 

135 no_args_is_help: bool = Default(False), 

136 subcommand_metavar: Optional[str] = Default(None), 

137 chain: bool = Default(False), 

138 result_callback: Optional[Callable[..., Any]] = Default(None), 

139 # Command 

140 context_settings: Optional[Dict[Any, Any]] = Default(None), 

141 callback: Optional[Callable[..., Any]] = Default(None), 

142 help: Optional[str] = Default(None), 

143 epilog: Optional[str] = Default(None), 

144 short_help: Optional[str] = Default(None), 

145 options_metavar: str = Default("[OPTIONS]"), 

146 add_help_option: bool = Default(True), 

147 hidden: bool = Default(False), 

148 deprecated: bool = Default(False), 

149 add_completion: bool = True, 

150 # Rich settings 

151 rich_markup_mode: MarkupMode = Default(DEFAULT_MARKUP_MODE), 

152 rich_help_panel: Union[str, None] = Default(None), 

153 pretty_exceptions_enable: bool = True, 

154 pretty_exceptions_show_locals: bool = True, 

155 pretty_exceptions_short: bool = True, 

156 ): 

157 self._add_completion = add_completion 1iceafgbdh

158 self.rich_markup_mode: MarkupMode = rich_markup_mode 1iceafgbdh

159 self.rich_help_panel = rich_help_panel 1iceafgbdh

160 self.pretty_exceptions_enable = pretty_exceptions_enable 1iceafgbdh

161 self.pretty_exceptions_show_locals = pretty_exceptions_show_locals 1iceafgbdh

162 self.pretty_exceptions_short = pretty_exceptions_short 1iceafgbdh

163 self.info = TyperInfo( 1iceafgbdh

164 name=name, 

165 cls=cls, 

166 invoke_without_command=invoke_without_command, 

167 no_args_is_help=no_args_is_help, 

168 subcommand_metavar=subcommand_metavar, 

169 chain=chain, 

170 result_callback=result_callback, 

171 context_settings=context_settings, 

172 callback=callback, 

173 help=help, 

174 epilog=epilog, 

175 short_help=short_help, 

176 options_metavar=options_metavar, 

177 add_help_option=add_help_option, 

178 hidden=hidden, 

179 deprecated=deprecated, 

180 ) 

181 self.registered_groups: List[TyperInfo] = [] 1iceafgbdh

182 self.registered_commands: List[CommandInfo] = [] 1iceafgbdh

183 self.registered_callback: Optional[TyperInfo] = None 1iceafgbdh

184 

185 def callback( 1ceafgbdh

186 self, 

187 *, 

188 cls: Optional[Type[TyperGroup]] = Default(None), 

189 invoke_without_command: bool = Default(False), 

190 no_args_is_help: bool = Default(False), 

191 subcommand_metavar: Optional[str] = Default(None), 

192 chain: bool = Default(False), 

193 result_callback: Optional[Callable[..., Any]] = Default(None), 

194 # Command 

195 context_settings: Optional[Dict[Any, Any]] = Default(None), 

196 help: Optional[str] = Default(None), 

197 epilog: Optional[str] = Default(None), 

198 short_help: Optional[str] = Default(None), 

199 options_metavar: str = Default("[OPTIONS]"), 

200 add_help_option: bool = Default(True), 

201 hidden: bool = Default(False), 

202 deprecated: bool = Default(False), 

203 # Rich settings 

204 rich_help_panel: Union[str, None] = Default(None), 

205 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 

206 def decorator(f: CommandFunctionType) -> CommandFunctionType: 1iceafgbdh

207 self.registered_callback = TyperInfo( 1iceafgbdh

208 cls=cls, 

209 invoke_without_command=invoke_without_command, 

210 no_args_is_help=no_args_is_help, 

211 subcommand_metavar=subcommand_metavar, 

212 chain=chain, 

213 result_callback=result_callback, 

214 context_settings=context_settings, 

215 callback=f, 

216 help=help, 

217 epilog=epilog, 

218 short_help=short_help, 

219 options_metavar=options_metavar, 

220 add_help_option=add_help_option, 

221 hidden=hidden, 

222 deprecated=deprecated, 

223 rich_help_panel=rich_help_panel, 

224 ) 

225 return f 1iceafgbdh

226 

227 return decorator 1iceafgbdh

228 

229 def command( 1ceafgbdh

230 self, 

231 name: Optional[str] = None, 

232 *, 

233 cls: Optional[Type[TyperCommand]] = None, 

234 context_settings: Optional[Dict[Any, Any]] = None, 

235 help: Optional[str] = None, 

236 epilog: Optional[str] = None, 

237 short_help: Optional[str] = None, 

238 options_metavar: str = "[OPTIONS]", 

239 add_help_option: bool = True, 

240 no_args_is_help: bool = False, 

241 hidden: bool = False, 

242 deprecated: bool = False, 

243 # Rich settings 

244 rich_help_panel: Union[str, None] = Default(None), 

245 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 

246 if cls is None: 1iceafgbdh

247 cls = TyperCommand 1iceafgbdh

248 

249 def decorator(f: CommandFunctionType) -> CommandFunctionType: 1iceafgbdh

250 self.registered_commands.append( 1iceafgbdh

251 CommandInfo( 

252 name=name, 

253 cls=cls, 

254 context_settings=context_settings, 

255 callback=f, 

256 help=help, 

257 epilog=epilog, 

258 short_help=short_help, 

259 options_metavar=options_metavar, 

260 add_help_option=add_help_option, 

261 no_args_is_help=no_args_is_help, 

262 hidden=hidden, 

263 deprecated=deprecated, 

264 # Rich settings 

265 rich_help_panel=rich_help_panel, 

266 ) 

267 ) 

268 return f 1iceafgbdh

269 

270 return decorator 1iceafgbdh

271 

272 def add_typer( 1ceafgbdh

273 self, 

274 typer_instance: "Typer", 

275 *, 

276 name: Optional[str] = Default(None), 

277 cls: Optional[Type[TyperGroup]] = Default(None), 

278 invoke_without_command: bool = Default(False), 

279 no_args_is_help: bool = Default(False), 

280 subcommand_metavar: Optional[str] = Default(None), 

281 chain: bool = Default(False), 

282 result_callback: Optional[Callable[..., Any]] = Default(None), 

283 # Command 

284 context_settings: Optional[Dict[Any, Any]] = Default(None), 

285 callback: Optional[Callable[..., Any]] = Default(None), 

286 help: Optional[str] = Default(None), 

287 epilog: Optional[str] = Default(None), 

288 short_help: Optional[str] = Default(None), 

289 options_metavar: str = Default("[OPTIONS]"), 

290 add_help_option: bool = Default(True), 

291 hidden: bool = Default(False), 

292 deprecated: bool = Default(False), 

293 # Rich settings 

294 rich_help_panel: Union[str, None] = Default(None), 

295 ) -> None: 

296 self.registered_groups.append( 1iceafgbdh

297 TyperInfo( 

298 typer_instance, 

299 name=name, 

300 cls=cls, 

301 invoke_without_command=invoke_without_command, 

302 no_args_is_help=no_args_is_help, 

303 subcommand_metavar=subcommand_metavar, 

304 chain=chain, 

305 result_callback=result_callback, 

306 context_settings=context_settings, 

307 callback=callback, 

308 help=help, 

309 epilog=epilog, 

310 short_help=short_help, 

311 options_metavar=options_metavar, 

312 add_help_option=add_help_option, 

313 hidden=hidden, 

314 deprecated=deprecated, 

315 rich_help_panel=rich_help_panel, 

316 ) 

317 ) 

318 

319 def __call__(self, *args: Any, **kwargs: Any) -> Any: 1iceafgbdh

320 if sys.excepthook != except_hook: 1iceafgbdh

321 sys.excepthook = except_hook 1iceafgbdh

322 try: 1iceafgbdh

323 return get_command(self)(*args, **kwargs) 1iceafgbdh

324 except Exception as e: 1iceafgbdh

325 # Set a custom attribute to tell the hook to show nice exceptions for user 

326 # code. An alternative/first implementation was a custom exception with 

327 # raise custom_exc from e 

328 # but that means the last error shown is the custom exception, not the 

329 # actual error. This trick improves developer experience by showing the 

330 # actual error last. 

331 setattr( 1iceafgbdh

332 e, 

333 _typer_developer_exception_attr_name, 

334 DeveloperExceptionConfig( 

335 pretty_exceptions_enable=self.pretty_exceptions_enable, 

336 pretty_exceptions_show_locals=self.pretty_exceptions_show_locals, 

337 pretty_exceptions_short=self.pretty_exceptions_short, 

338 ), 

339 ) 

340 raise e 1iceafgbdh

341 

342 

343def get_group(typer_instance: Typer) -> TyperGroup: 1iceafgbdh

344 group = get_group_from_info( 1iceafgbdh

345 TyperInfo(typer_instance), 

346 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 

347 rich_markup_mode=typer_instance.rich_markup_mode, 

348 ) 

349 return group 1iceafgbdh

350 

351 

352def get_command(typer_instance: Typer) -> click.Command: 1iceafgbdh

353 if typer_instance._add_completion: 1iceafgbdh

354 click_install_param, click_show_param = get_install_completion_arguments() 1iceafgbdh

355 if ( 1cabd

356 typer_instance.registered_callback 

357 or typer_instance.info.callback 

358 or typer_instance.registered_groups 

359 or len(typer_instance.registered_commands) > 1 

360 ): 

361 # Create a Group 

362 click_command: click.Command = get_group(typer_instance) 1iceafgbdh

363 if typer_instance._add_completion: 1iceafgbdh

364 click_command.params.append(click_install_param) 1iceafgbdh

365 click_command.params.append(click_show_param) 1iceafgbdh

366 return click_command 1iceafgbdh

367 elif len(typer_instance.registered_commands) == 1: 1iceafgbdh

368 # Create a single Command 

369 single_command = typer_instance.registered_commands[0] 1iceafgbdh

370 

371 if not single_command.context_settings and not isinstance( 1iceafgbdh

372 typer_instance.info.context_settings, DefaultPlaceholder 

373 ): 

374 single_command.context_settings = typer_instance.info.context_settings 1iceafgbdh

375 

376 click_command = get_command_from_info( 1iceafgbdh

377 single_command, 

378 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 

379 rich_markup_mode=typer_instance.rich_markup_mode, 

380 ) 

381 if typer_instance._add_completion: 1iceafgbdh

382 click_command.params.append(click_install_param) 1iceafgbdh

383 click_command.params.append(click_show_param) 1iceafgbdh

384 return click_command 1iceafgbdh

385 raise RuntimeError( 

386 "Could not get a command for this Typer instance" 

387 ) # pragma: no cover 

388 

389 

390def solve_typer_info_help(typer_info: TyperInfo) -> str: 1iceafgbdh

391 # Priority 1: Explicit value was set in app.add_typer() 

392 if not isinstance(typer_info.help, DefaultPlaceholder): 1iceafgbdh

393 return inspect.cleandoc(typer_info.help or "") 1iceafgbdh

394 # Priority 2: Explicit value was set in sub_app.callback() 

395 try: 1iceafgbdh

396 callback_help = typer_info.typer_instance.registered_callback.help 1iceafgbdh

397 if not isinstance(callback_help, DefaultPlaceholder): 1iceafgbdh

398 return inspect.cleandoc(callback_help or "") 1iceafgbdh

399 except AttributeError: 1iceafgbdh

400 pass 1iceafgbdh

401 # Priority 3: Explicit value was set in sub_app = typer.Typer() 

402 try: 1iceafgbdh

403 instance_help = typer_info.typer_instance.info.help 1iceafgbdh

404 if not isinstance(instance_help, DefaultPlaceholder): 1iceafgbdh

405 return inspect.cleandoc(instance_help or "") 1iceafgbdh

406 except AttributeError: 1iceafgbdh

407 pass 1iceafgbdh

408 # Priority 4: Implicit inference from callback docstring in app.add_typer() 

409 if typer_info.callback: 1iceafgbdh

410 doc = inspect.getdoc(typer_info.callback) 1iceafgbdh

411 if doc: 1iceafgbdh

412 return doc 1iceafgbdh

413 # Priority 5: Implicit inference from callback docstring in @app.callback() 

414 try: 1iceafgbdh

415 callback = typer_info.typer_instance.registered_callback.callback 1iceafgbdh

416 if not isinstance(callback, DefaultPlaceholder): 1iceafgbdh

417 doc = inspect.getdoc(callback or "") 1iceafgbdh

418 if doc: 1iceafgbdh

419 return doc 1iceafgbdh

420 except AttributeError: 1iceafgbdh

421 pass 1iceafgbdh

422 # Priority 6: Implicit inference from callback docstring in typer.Typer() 

423 try: 1iceafgbdh

424 instance_callback = typer_info.typer_instance.info.callback 1iceafgbdh

425 if not isinstance(instance_callback, DefaultPlaceholder): 1iceafgbdh

426 doc = inspect.getdoc(instance_callback) 1iceafgbdh

427 if doc: 1iceafgbdh

428 return doc 1iceafgbdh

429 except AttributeError: 1iceafgbdh

430 pass 1iceafgbdh

431 # Value not set, use the default 

432 return typer_info.help.value 1iceafgbdh

433 

434 

435def solve_typer_info_defaults(typer_info: TyperInfo) -> TyperInfo: 1iceafgbdh

436 values: Dict[str, Any] = {} 1iceafgbdh

437 for name, value in typer_info.__dict__.items(): 1iceafgbdh

438 # Priority 1: Value was set in app.add_typer() 

439 if not isinstance(value, DefaultPlaceholder): 1iceafgbdh

440 values[name] = value 1iceafgbdh

441 continue 1iceafgbdh

442 # Priority 2: Value was set in @subapp.callback() 

443 try: 1iceafgbdh

444 callback_value = getattr( 1iceafgbdh

445 typer_info.typer_instance.registered_callback, # type: ignore 

446 name, 

447 ) 

448 if not isinstance(callback_value, DefaultPlaceholder): 1iceafgbdh

449 values[name] = callback_value 1iceafgbdh

450 continue 1iceafgbdh

451 except AttributeError: 1iceafgbdh

452 pass 1iceafgbdh

453 # Priority 3: Value set in subapp = typer.Typer() 

454 try: 1iceafgbdh

455 instance_value = getattr( 1iceafgbdh

456 typer_info.typer_instance.info, # type: ignore 

457 name, 

458 ) 

459 if not isinstance(instance_value, DefaultPlaceholder): 1iceafgbdh

460 values[name] = instance_value 1iceafgbdh

461 continue 1iceafgbdh

462 except AttributeError: 1iceafgbdh

463 pass 1iceafgbdh

464 # Value not set, use the default 

465 values[name] = value.value 1iceafgbdh

466 values["help"] = solve_typer_info_help(typer_info) 1iceafgbdh

467 return TyperInfo(**values) 1iceafgbdh

468 

469 

470def get_group_from_info( 1ceafgbdh

471 group_info: TyperInfo, 

472 *, 

473 pretty_exceptions_short: bool, 

474 rich_markup_mode: MarkupMode, 

475) -> TyperGroup: 

476 assert group_info.typer_instance, ( 1iceafgbdh

477 "A Typer instance is needed to generate a Click Group" 

478 ) 

479 commands: Dict[str, click.Command] = {} 1iceafgbdh

480 for command_info in group_info.typer_instance.registered_commands: 1iceafgbdh

481 command = get_command_from_info( 1iceafgbdh

482 command_info=command_info, 

483 pretty_exceptions_short=pretty_exceptions_short, 

484 rich_markup_mode=rich_markup_mode, 

485 ) 

486 if command.name: 1iceafgbdh

487 commands[command.name] = command 1iceafgbdh

488 for sub_group_info in group_info.typer_instance.registered_groups: 1iceafgbdh

489 sub_group = get_group_from_info( 1iceafgbdh

490 sub_group_info, 

491 pretty_exceptions_short=pretty_exceptions_short, 

492 rich_markup_mode=rich_markup_mode, 

493 ) 

494 if sub_group.name: 1iceafgbdh

495 commands[sub_group.name] = sub_group 1iceafgbdh

496 else: 

497 if sub_group.callback: 1iceafgbdh

498 import warnings 1iceafgbdh

499 

500 warnings.warn( 1iceafgbdh

501 "The 'callback' parameter is not supported by Typer when using `add_typer` without a name", 

502 stacklevel=5, 

503 ) 

504 for sub_command_name, sub_command in sub_group.commands.items(): 1iceafgbdh

505 commands[sub_command_name] = sub_command 1iceafgbdh

506 solved_info = solve_typer_info_defaults(group_info) 1iceafgbdh

507 ( 1ceafgbdh

508 params, 

509 convertors, 

510 context_param_name, 

511 ) = get_params_convertors_ctx_param_name_from_function(solved_info.callback) 

512 cls = solved_info.cls or TyperGroup 1iceafgbdh

513 assert issubclass(cls, TyperGroup), f"{cls} should be a subclass of {TyperGroup}" 1iceafgbdh

514 group = cls( 1iceafgbdh

515 name=solved_info.name or "", 

516 commands=commands, 

517 invoke_without_command=solved_info.invoke_without_command, 

518 no_args_is_help=solved_info.no_args_is_help, 

519 subcommand_metavar=solved_info.subcommand_metavar, 

520 chain=solved_info.chain, 

521 result_callback=solved_info.result_callback, 

522 context_settings=solved_info.context_settings, 

523 callback=get_callback( 

524 callback=solved_info.callback, 

525 params=params, 

526 convertors=convertors, 

527 context_param_name=context_param_name, 

528 pretty_exceptions_short=pretty_exceptions_short, 

529 ), 

530 params=params, 

531 help=solved_info.help, 

532 epilog=solved_info.epilog, 

533 short_help=solved_info.short_help, 

534 options_metavar=solved_info.options_metavar, 

535 add_help_option=solved_info.add_help_option, 

536 hidden=solved_info.hidden, 

537 deprecated=solved_info.deprecated, 

538 rich_markup_mode=rich_markup_mode, 

539 # Rich settings 

540 rich_help_panel=solved_info.rich_help_panel, 

541 ) 

542 return group 1iceafgbdh

543 

544 

545def get_command_name(name: str) -> str: 1iceafgbdh

546 return name.lower().replace("_", "-") 1iceafgbdh

547 

548 

549def get_params_convertors_ctx_param_name_from_function( 1ceafgbdh

550 callback: Optional[Callable[..., Any]], 

551) -> Tuple[List[Union[click.Argument, click.Option]], Dict[str, Any], Optional[str]]: 

552 params = [] 1iceafgbdh

553 convertors = {} 1iceafgbdh

554 context_param_name = None 1iceafgbdh

555 if callback: 1iceafgbdh

556 parameters = get_params_from_function(callback) 1iceafgbdh

557 for param_name, param in parameters.items(): 1iceafgbdh

558 if lenient_issubclass(param.annotation, click.Context): 1iceafgbdh

559 context_param_name = param_name 1iceafgbdh

560 continue 1iceafgbdh

561 click_param, convertor = get_click_param(param) 1iceafgbdh

562 if convertor: 1iceafgbdh

563 convertors[param_name] = convertor 1iceafgbdh

564 params.append(click_param) 1iceafgbdh

565 return params, convertors, context_param_name 1iceafgbdh

566 

567 

568def get_command_from_info( 1ceafgbdh

569 command_info: CommandInfo, 

570 *, 

571 pretty_exceptions_short: bool, 

572 rich_markup_mode: MarkupMode, 

573) -> click.Command: 

574 assert command_info.callback, "A command must have a callback function" 1iceafgbdh

575 name = command_info.name or get_command_name(command_info.callback.__name__) 1iceafgbdh

576 use_help = command_info.help 1iceafgbdh

577 if use_help is None: 1iceafgbdh

578 use_help = inspect.getdoc(command_info.callback) 1iceafgbdh

579 else: 

580 use_help = inspect.cleandoc(use_help) 1iceafgbdh

581 ( 1ceafgbdh

582 params, 

583 convertors, 

584 context_param_name, 

585 ) = get_params_convertors_ctx_param_name_from_function(command_info.callback) 

586 cls = command_info.cls or TyperCommand 1iceafgbdh

587 command = cls( 1iceafgbdh

588 name=name, 

589 context_settings=command_info.context_settings, 

590 callback=get_callback( 

591 callback=command_info.callback, 

592 params=params, 

593 convertors=convertors, 

594 context_param_name=context_param_name, 

595 pretty_exceptions_short=pretty_exceptions_short, 

596 ), 

597 params=params, # type: ignore 

598 help=use_help, 

599 epilog=command_info.epilog, 

600 short_help=command_info.short_help, 

601 options_metavar=command_info.options_metavar, 

602 add_help_option=command_info.add_help_option, 

603 no_args_is_help=command_info.no_args_is_help, 

604 hidden=command_info.hidden, 

605 deprecated=command_info.deprecated, 

606 rich_markup_mode=rich_markup_mode, 

607 # Rich settings 

608 rich_help_panel=command_info.rich_help_panel, 

609 ) 

610 return command 1iceafgbdh

611 

612 

613def determine_type_convertor(type_: Any) -> Optional[Callable[[Any], Any]]: 1iceafgbdh

614 convertor: Optional[Callable[[Any], Any]] = None 1iceafgbdh

615 if lenient_issubclass(type_, Path): 1iceafgbdh

616 convertor = param_path_convertor 1iceafgbdh

617 if lenient_issubclass(type_, Enum): 1iceafgbdh

618 convertor = generate_enum_convertor(type_) 1iceafgbdh

619 return convertor 1iceafgbdh

620 

621 

622def param_path_convertor(value: Optional[str] = None) -> Optional[Path]: 1iceafgbdh

623 if value is not None: 1iceafgbdh

624 return Path(value) 1iceafgbdh

625 return None 1iceafgbdh

626 

627 

628def generate_enum_convertor(enum: Type[Enum]) -> Callable[[Any], Any]: 1iceafgbdh

629 val_map = {str(val.value): val for val in enum} 1iceafgbdh

630 

631 def convertor(value: Any) -> Any: 1iceafgbdh

632 if value is not None: 1iceafgbdh

633 val = str(value) 1iceafgbdh

634 if val in val_map: 1iceafgbdh

635 key = val_map[val] 1iceafgbdh

636 return enum(key) 1iceafgbdh

637 

638 return convertor 1iceafgbdh

639 

640 

641def generate_list_convertor( 1ceafgbdh

642 convertor: Optional[Callable[[Any], Any]], default_value: Optional[Any] 

643) -> Callable[[Sequence[Any]], Optional[List[Any]]]: 

644 def internal_convertor(value: Sequence[Any]) -> Optional[List[Any]]: 1iceafgbdh

645 if default_value is None and len(value) == 0: 1iceafgbdh

646 return None 1iceafgbdh

647 return [convertor(v) if convertor else v for v in value] 1iceafgbdh

648 

649 return internal_convertor 1iceafgbdh

650 

651 

652def generate_tuple_convertor( 1ceafgbdh

653 types: Sequence[Any], 

654) -> Callable[[Optional[Tuple[Any, ...]]], Optional[Tuple[Any, ...]]]: 

655 convertors = [determine_type_convertor(type_) for type_ in types] 1iceafgbdh

656 

657 def internal_convertor( 1ceafgbdh

658 param_args: Optional[Tuple[Any, ...]], 

659 ) -> Optional[Tuple[Any, ...]]: 

660 if param_args is None: 1iceafgbdh

661 return None 1iceafgbdh

662 return tuple( 1iceafgbdh

663 convertor(arg) if convertor else arg 

664 for (convertor, arg) in zip(convertors, param_args) 

665 ) 

666 

667 return internal_convertor 1iceafgbdh

668 

669 

670def get_callback( 1ceafgbdh

671 *, 

672 callback: Optional[Callable[..., Any]] = None, 

673 params: Sequence[click.Parameter] = [], 

674 convertors: Optional[Dict[str, Callable[[str], Any]]] = None, 

675 context_param_name: Optional[str] = None, 

676 pretty_exceptions_short: bool, 

677) -> Optional[Callable[..., Any]]: 

678 use_convertors = convertors or {} 1iceafgbdh

679 if not callback: 1iceafgbdh

680 return None 1iceafgbdh

681 parameters = get_params_from_function(callback) 1iceafgbdh

682 use_params: Dict[str, Any] = {} 1iceafgbdh

683 for param_name in parameters: 1iceafgbdh

684 use_params[param_name] = None 1iceafgbdh

685 for param in params: 1iceafgbdh

686 if param.name: 1iceafgbdh

687 use_params[param.name] = param.default 1iceafgbdh

688 

689 def wrapper(**kwargs: Any) -> Any: 1iceafgbdh

690 _rich_traceback_guard = pretty_exceptions_short # noqa: F841 1iceafgbdh

691 for k, v in kwargs.items(): 1iceafgbdh

692 if k in use_convertors: 1iceafgbdh

693 use_params[k] = use_convertors[k](v) 1iceafgbdh

694 else: 

695 use_params[k] = v 1iceafgbdh

696 if context_param_name: 1iceafgbdh

697 use_params[context_param_name] = click.get_current_context() 1iceafgbdh

698 return callback(**use_params) 1iceafgbdh

699 

700 update_wrapper(wrapper, callback) 1iceafgbdh

701 return wrapper 1iceafgbdh

702 

703 

704def get_click_type( 1ceafgbdh

705 *, annotation: Any, parameter_info: ParameterInfo 

706) -> click.ParamType: 

707 if parameter_info.click_type is not None: 1iceafgbdh

708 return parameter_info.click_type 1iceafgbdh

709 

710 elif parameter_info.parser is not None: 1iceafgbdh

711 return click.types.FuncParamType(parameter_info.parser) 1iceafgbdh

712 

713 elif annotation is str: 1iceafgbdh

714 return click.STRING 1iceafgbdh

715 elif annotation is int: 1iceafgbdh

716 if parameter_info.min is not None or parameter_info.max is not None: 1iceafgbdh

717 min_ = None 1iceafgbdh

718 max_ = None 1iceafgbdh

719 if parameter_info.min is not None: 1iceafgbdh

720 min_ = int(parameter_info.min) 1iceafgbdh

721 if parameter_info.max is not None: 1iceafgbdh

722 max_ = int(parameter_info.max) 1iceafgbdh

723 return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp) 1iceafgbdh

724 else: 

725 return click.INT 1iceafgbdh

726 elif annotation is float: 1iceafgbdh

727 if parameter_info.min is not None or parameter_info.max is not None: 1iceafgbdh

728 return click.FloatRange( 1iceafgbdh

729 min=parameter_info.min, 

730 max=parameter_info.max, 

731 clamp=parameter_info.clamp, 

732 ) 

733 else: 

734 return click.FLOAT 1iceafgbdh

735 elif annotation is bool: 1iceafgbdh

736 return click.BOOL 1iceafgbdh

737 elif annotation == UUID: 1iceafgbdh

738 return click.UUID 1iceafgbdh

739 elif annotation == datetime: 1iceafgbdh

740 return click.DateTime(formats=parameter_info.formats) 1iceafgbdh

741 elif ( 1ab

742 annotation == Path 

743 or parameter_info.allow_dash 

744 or parameter_info.path_type 

745 or parameter_info.resolve_path 

746 ): 

747 return TyperPath( 1iceafgbdh

748 exists=parameter_info.exists, 

749 file_okay=parameter_info.file_okay, 

750 dir_okay=parameter_info.dir_okay, 

751 writable=parameter_info.writable, 

752 readable=parameter_info.readable, 

753 resolve_path=parameter_info.resolve_path, 

754 allow_dash=parameter_info.allow_dash, 

755 path_type=parameter_info.path_type, 

756 ) 

757 elif lenient_issubclass(annotation, FileTextWrite): 1iceafgbdh

758 return click.File( 1iceafgbdh

759 mode=parameter_info.mode or "w", 

760 encoding=parameter_info.encoding, 

761 errors=parameter_info.errors, 

762 lazy=parameter_info.lazy, 

763 atomic=parameter_info.atomic, 

764 ) 

765 elif lenient_issubclass(annotation, FileText): 1iceafgbdh

766 return click.File( 1iceafgbdh

767 mode=parameter_info.mode or "r", 

768 encoding=parameter_info.encoding, 

769 errors=parameter_info.errors, 

770 lazy=parameter_info.lazy, 

771 atomic=parameter_info.atomic, 

772 ) 

773 elif lenient_issubclass(annotation, FileBinaryRead): 1iceafgbdh

774 return click.File( 1iceafgbdh

775 mode=parameter_info.mode or "rb", 

776 encoding=parameter_info.encoding, 

777 errors=parameter_info.errors, 

778 lazy=parameter_info.lazy, 

779 atomic=parameter_info.atomic, 

780 ) 

781 elif lenient_issubclass(annotation, FileBinaryWrite): 1iceafgbdh

782 return click.File( 1iceafgbdh

783 mode=parameter_info.mode or "wb", 

784 encoding=parameter_info.encoding, 

785 errors=parameter_info.errors, 

786 lazy=parameter_info.lazy, 

787 atomic=parameter_info.atomic, 

788 ) 

789 elif lenient_issubclass(annotation, Enum): 1iceafgbdh

790 return click.Choice( 1iceafgbdh

791 [item.value for item in annotation], 

792 case_sensitive=parameter_info.case_sensitive, 

793 ) 

794 raise RuntimeError(f"Type not yet supported: {annotation}") # pragma: no cover 

795 

796 

797def lenient_issubclass( 1ceafgbdh

798 cls: Any, class_or_tuple: Union[AnyType, Tuple[AnyType, ...]] 

799) -> bool: 

800 return isinstance(cls, type) and issubclass(cls, class_or_tuple) 1iceafgbdh

801 

802 

803def get_click_param( 1ceafgbdh

804 param: ParamMeta, 

805) -> Tuple[Union[click.Argument, click.Option], Any]: 

806 # First, find out what will be: 

807 # * ParamInfo (ArgumentInfo or OptionInfo) 

808 # * default_value 

809 # * required 

810 default_value = None 1iceafgbdh

811 required = False 1iceafgbdh

812 if isinstance(param.default, ParameterInfo): 1iceafgbdh

813 parameter_info = param.default 1iceafgbdh

814 if parameter_info.default == Required: 1iceafgbdh

815 required = True 1iceafgbdh

816 else: 

817 default_value = parameter_info.default 1iceafgbdh

818 elif param.default == Required or param.default is param.empty: 1iceafgbdh

819 required = True 1iceafgbdh

820 parameter_info = ArgumentInfo() 1iceafgbdh

821 else: 

822 default_value = param.default 1iceafgbdh

823 parameter_info = OptionInfo() 1iceafgbdh

824 annotation: Any 

825 if param.annotation is not param.empty: 1iceafgbdh

826 annotation = param.annotation 1iceafgbdh

827 else: 

828 annotation = str 1iceafgbdh

829 main_type = annotation 1iceafgbdh

830 is_list = False 1iceafgbdh

831 is_tuple = False 1iceafgbdh

832 parameter_type: Any = None 1iceafgbdh

833 is_flag = None 1iceafgbdh

834 origin = get_origin(main_type) 1iceafgbdh

835 

836 if origin is not None: 1iceafgbdh

837 # Handle SomeType | None and Optional[SomeType] 

838 if is_union(origin): 1iceafgbdh

839 types = [] 1iceafgbdh

840 for type_ in get_args(main_type): 1iceafgbdh

841 if type_ is NoneType: 1iceafgbdh

842 continue 1iceafgbdh

843 types.append(type_) 1iceafgbdh

844 assert len(types) == 1, "Typer Currently doesn't support Union types" 1iceafgbdh

845 main_type = types[0] 1iceafgbdh

846 origin = get_origin(main_type) 1iceafgbdh

847 # Handle Tuples and Lists 

848 if lenient_issubclass(origin, List): 1iceafgbdh

849 main_type = get_args(main_type)[0] 1iceafgbdh

850 assert not get_origin(main_type), ( 1iceafgbdh

851 "List types with complex sub-types are not currently supported" 

852 ) 

853 is_list = True 1iceafgbdh

854 elif lenient_issubclass(origin, Tuple): # type: ignore 1iceafgbdh

855 types = [] 1iceafgbdh

856 for type_ in get_args(main_type): 1iceafgbdh

857 assert not get_origin(type_), ( 1iceafgbdh

858 "Tuple types with complex sub-types are not currently supported" 

859 ) 

860 types.append( 1iceafgbdh

861 get_click_type(annotation=type_, parameter_info=parameter_info) 

862 ) 

863 parameter_type = tuple(types) 1iceafgbdh

864 is_tuple = True 1iceafgbdh

865 if parameter_type is None: 1iceafgbdh

866 parameter_type = get_click_type( 1iceafgbdh

867 annotation=main_type, parameter_info=parameter_info 

868 ) 

869 convertor = determine_type_convertor(main_type) 1iceafgbdh

870 if is_list: 1iceafgbdh

871 convertor = generate_list_convertor( 1iceafgbdh

872 convertor=convertor, default_value=default_value 

873 ) 

874 if is_tuple: 1iceafgbdh

875 convertor = generate_tuple_convertor(get_args(main_type)) 1iceafgbdh

876 if isinstance(parameter_info, OptionInfo): 1iceafgbdh

877 if main_type is bool: 1iceafgbdh

878 is_flag = True 1iceafgbdh

879 # Click doesn't accept a flag of type bool, only None, and then it sets it 

880 # to bool internally 

881 parameter_type = None 1iceafgbdh

882 default_option_name = get_command_name(param.name) 1iceafgbdh

883 if is_flag: 1iceafgbdh

884 default_option_declaration = ( 1ceafgbdh

885 f"--{default_option_name}/--no-{default_option_name}" 

886 ) 

887 else: 

888 default_option_declaration = f"--{default_option_name}" 1iceafgbdh

889 param_decls = [param.name] 1iceafgbdh

890 if parameter_info.param_decls: 1iceafgbdh

891 param_decls.extend(parameter_info.param_decls) 1iceafgbdh

892 else: 

893 param_decls.append(default_option_declaration) 1iceafgbdh

894 return ( 1ceafgbdh

895 TyperOption( 

896 # Option 

897 param_decls=param_decls, 

898 show_default=parameter_info.show_default, 

899 prompt=parameter_info.prompt, 

900 confirmation_prompt=parameter_info.confirmation_prompt, 

901 prompt_required=parameter_info.prompt_required, 

902 hide_input=parameter_info.hide_input, 

903 is_flag=is_flag, 

904 multiple=is_list, 

905 count=parameter_info.count, 

906 allow_from_autoenv=parameter_info.allow_from_autoenv, 

907 type=parameter_type, 

908 help=parameter_info.help, 

909 hidden=parameter_info.hidden, 

910 show_choices=parameter_info.show_choices, 

911 show_envvar=parameter_info.show_envvar, 

912 # Parameter 

913 required=required, 

914 default=default_value, 

915 callback=get_param_callback( 

916 callback=parameter_info.callback, convertor=convertor 

917 ), 

918 metavar=parameter_info.metavar, 

919 expose_value=parameter_info.expose_value, 

920 is_eager=parameter_info.is_eager, 

921 envvar=parameter_info.envvar, 

922 shell_complete=parameter_info.shell_complete, 

923 autocompletion=get_param_completion(parameter_info.autocompletion), 

924 # Rich settings 

925 rich_help_panel=parameter_info.rich_help_panel, 

926 ), 

927 convertor, 

928 ) 

929 elif isinstance(parameter_info, ArgumentInfo): 1iceafgbdh

930 param_decls = [param.name] 1iceafgbdh

931 nargs = None 1iceafgbdh

932 if is_list: 1iceafgbdh

933 nargs = -1 1iceafgbdh

934 return ( 1ceafgbdh

935 TyperArgument( 

936 # Argument 

937 param_decls=param_decls, 

938 type=parameter_type, 

939 required=required, 

940 nargs=nargs, 

941 # TyperArgument 

942 show_default=parameter_info.show_default, 

943 show_choices=parameter_info.show_choices, 

944 show_envvar=parameter_info.show_envvar, 

945 help=parameter_info.help, 

946 hidden=parameter_info.hidden, 

947 # Parameter 

948 default=default_value, 

949 callback=get_param_callback( 

950 callback=parameter_info.callback, convertor=convertor 

951 ), 

952 metavar=parameter_info.metavar, 

953 expose_value=parameter_info.expose_value, 

954 is_eager=parameter_info.is_eager, 

955 envvar=parameter_info.envvar, 

956 shell_complete=parameter_info.shell_complete, 

957 autocompletion=get_param_completion(parameter_info.autocompletion), 

958 # Rich settings 

959 rich_help_panel=parameter_info.rich_help_panel, 

960 ), 

961 convertor, 

962 ) 

963 raise AssertionError("A click.Parameter should be returned") # pragma: no cover 

964 

965 

966def get_param_callback( 1ceafgbdh

967 *, 

968 callback: Optional[Callable[..., Any]] = None, 

969 convertor: Optional[Callable[..., Any]] = None, 

970) -> Optional[Callable[..., Any]]: 

971 if not callback: 1iceafgbdh

972 return None 1iceafgbdh

973 parameters = get_params_from_function(callback) 1iceafgbdh

974 ctx_name = None 1iceafgbdh

975 click_param_name = None 1iceafgbdh

976 value_name = None 1iceafgbdh

977 untyped_names: List[str] = [] 1iceafgbdh

978 for param_name, param_sig in parameters.items(): 1iceafgbdh

979 if lenient_issubclass(param_sig.annotation, click.Context): 1iceafgbdh

980 ctx_name = param_name 1iceafgbdh

981 elif lenient_issubclass(param_sig.annotation, click.Parameter): 1iceafgbdh

982 click_param_name = param_name 1iceafgbdh

983 else: 

984 untyped_names.append(param_name) 1iceafgbdh

985 # Extract value param name first 

986 if untyped_names: 1iceafgbdh

987 value_name = untyped_names.pop() 1iceafgbdh

988 # If context and Click param were not typed (old/Click callback style) extract them 

989 if untyped_names: 1iceafgbdh

990 if ctx_name is None: 1iceafgbdh

991 ctx_name = untyped_names.pop(0) 1iceafgbdh

992 if click_param_name is None: 1iceafgbdh

993 if untyped_names: 1iceafgbdh

994 click_param_name = untyped_names.pop(0) 1iceafgbdh

995 if untyped_names: 1iceafgbdh

996 raise click.ClickException( 1iceafgbdh

997 "Too many CLI parameter callback function parameters" 

998 ) 

999 

1000 def wrapper(ctx: click.Context, param: click.Parameter, value: Any) -> Any: 1iceafgbdh

1001 use_params: Dict[str, Any] = {} 1iceafgbdh

1002 if ctx_name: 1iceafgbdh

1003 use_params[ctx_name] = ctx 1iceafgbdh

1004 if click_param_name: 1iceafgbdh

1005 use_params[click_param_name] = param 1iceafgbdh

1006 if value_name: 1iceafgbdh

1007 if convertor: 1iceafgbdh

1008 use_value = convertor(value) 1iceafgbdh

1009 else: 

1010 use_value = value 1iceafgbdh

1011 use_params[value_name] = use_value 1iceafgbdh

1012 return callback(**use_params) 1iceafgbdh

1013 

1014 update_wrapper(wrapper, callback) 1iceafgbdh

1015 return wrapper 1iceafgbdh

1016 

1017 

1018def get_param_completion( 1ceafgbdh

1019 callback: Optional[Callable[..., Any]] = None, 

1020) -> Optional[Callable[..., Any]]: 

1021 if not callback: 1iceafgbdh

1022 return None 1iceafgbdh

1023 parameters = get_params_from_function(callback) 1iceafgbdh

1024 ctx_name = None 1iceafgbdh

1025 args_name = None 1iceafgbdh

1026 incomplete_name = None 1iceafgbdh

1027 unassigned_params = list(parameters.values()) 1iceafgbdh

1028 for param_sig in unassigned_params[:]: 1iceafgbdh

1029 origin = get_origin(param_sig.annotation) 1iceafgbdh

1030 if lenient_issubclass(param_sig.annotation, click.Context): 1iceafgbdh

1031 ctx_name = param_sig.name 1iceafgbdh

1032 unassigned_params.remove(param_sig) 1iceafgbdh

1033 elif lenient_issubclass(origin, List): 1iceafgbdh

1034 args_name = param_sig.name 1iceafgbdh

1035 unassigned_params.remove(param_sig) 1iceafgbdh

1036 elif lenient_issubclass(param_sig.annotation, str): 1iceafgbdh

1037 incomplete_name = param_sig.name 1iceafgbdh

1038 unassigned_params.remove(param_sig) 1iceafgbdh

1039 # If there are still unassigned parameters (not typed), extract by name 

1040 for param_sig in unassigned_params[:]: 1iceafgbdh

1041 if ctx_name is None and param_sig.name == "ctx": 1iceafgbdh

1042 ctx_name = param_sig.name 1iceafgbdh

1043 unassigned_params.remove(param_sig) 1iceafgbdh

1044 elif args_name is None and param_sig.name == "args": 1iceafgbdh

1045 args_name = param_sig.name 1iceafgbdh

1046 unassigned_params.remove(param_sig) 1iceafgbdh

1047 elif incomplete_name is None and param_sig.name == "incomplete": 1iceafgbdh

1048 incomplete_name = param_sig.name 1iceafgbdh

1049 unassigned_params.remove(param_sig) 1iceafgbdh

1050 # Extract value param name first 

1051 if unassigned_params: 1iceafgbdh

1052 show_params = " ".join([param.name for param in unassigned_params]) 1iceafgbdh

1053 raise click.ClickException( 1iceafgbdh

1054 f"Invalid autocompletion callback parameters: {show_params}" 

1055 ) 

1056 

1057 def wrapper(ctx: click.Context, args: List[str], incomplete: Optional[str]) -> Any: 1iceafgbdh

1058 use_params: Dict[str, Any] = {} 1iceafgbdh

1059 if ctx_name: 1iceafgbdh

1060 use_params[ctx_name] = ctx 1iceafgbdh

1061 if args_name: 1iceafgbdh

1062 use_params[args_name] = args 1iceafgbdh

1063 if incomplete_name: 1iceafgbdh

1064 use_params[incomplete_name] = incomplete 1iceafgbdh

1065 return callback(**use_params) 1iceafgbdh

1066 

1067 update_wrapper(wrapper, callback) 1iceafgbdh

1068 return wrapper 1iceafgbdh

1069 

1070 

1071def run(function: Callable[..., Any]) -> None: 1iceafgbdh

1072 app = Typer(add_completion=False) 1iceafgbdh

1073 app.command()(function) 1iceafgbdh

1074 app() 1iceafgbdh

1075 

1076 

1077def _is_macos() -> bool: 1iceafgbdh

1078 return platform.system() == "Darwin" 1iceafgbdh

1079 

1080 

1081def _is_linux_or_bsd() -> bool: 1iceafgbdh

1082 if platform.system() == "Linux": 1iceafgbdh

1083 return True 1iceafgbdh

1084 

1085 return "BSD" in platform.system() 1iceafgbdh

1086 

1087 

1088def launch(url: str, wait: bool = False, locate: bool = False) -> int: 1iceafgbdh

1089 """This function launches the given URL (or filename) in the default 

1090 viewer application for this file type. If this is an executable, it 

1091 might launch the executable in a new session. The return value is 

1092 the exit code of the launched application. Usually, ``0`` indicates 

1093 success. 

1094 

1095 This function handles url in different operating systems separately: 

1096 - On macOS (Darwin), it uses the 'open' command. 

1097 - On Linux and BSD, it uses 'xdg-open' if available. 

1098 - On Windows (and other OSes), it uses the standard webbrowser module. 

1099 

1100 The function avoids, when possible, using the webbrowser module on Linux and macOS 

1101 to prevent spammy terminal messages from some browsers (e.g., Chrome). 

1102 

1103 Examples:: 

1104 

1105 typer.launch("https://typer.tiangolo.com/") 

1106 typer.launch("/my/downloaded/file", locate=True) 

1107 

1108 :param url: URL or filename of the thing to launch. 

1109 :param wait: Wait for the program to exit before returning. This 

1110 only works if the launched program blocks. In particular, 

1111 ``xdg-open`` on Linux does not block. 

1112 :param locate: if this is set to `True` then instead of launching the 

1113 application associated with the URL it will attempt to 

1114 launch a file manager with the file located. This 

1115 might have weird effects if the URL does not point to 

1116 the filesystem. 

1117 """ 

1118 

1119 if url.startswith("http://") or url.startswith("https://"): 1iceafgbdh

1120 if _is_macos(): 1iceafgbdh

1121 return subprocess.Popen( 1iceafgbdh

1122 ["open", url], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT 

1123 ).wait() 

1124 

1125 has_xdg_open = _is_linux_or_bsd() and shutil.which("xdg-open") is not None 1iceafgbdh

1126 

1127 if has_xdg_open: 1iceafgbdh

1128 return subprocess.Popen( 1iceafgbdh

1129 ["xdg-open", url], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT 

1130 ).wait() 

1131 

1132 import webbrowser 1iceafgbdh

1133 

1134 webbrowser.open(url) 1iceafgbdh

1135 

1136 return 0 1iceafgbdh

1137 

1138 else: 

1139 return click.launch(url) 1iceafgbdh