Coverage for typer/main.py: 100%

491 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-03-10 00:15 +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) 

48from .utils import get_params_from_function 1iceafgbdh

49 

50try: 1iceafgbdh

51 import rich 1iceafgbdh

52 from rich.traceback import Traceback 1iceafgbdh

53 

54 from . import rich_utils 1iceafgbdh

55 

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

57 

58except ImportError: # pragma: no cover 

59 rich = None # type: ignore 

60 

61_original_except_hook = sys.excepthook 1iceafgbdh

62_typer_developer_exception_attr_name = "__typer_developer_exception__" 1iceafgbdh

63 

64 

65def except_hook( 1ceafgbdh

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

67) -> None: 

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

69 exc_value, _typer_developer_exception_attr_name, None 

70 ) 

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

72 if ( 1cabd

73 standard_traceback 

74 or not exception_config 

75 or not exception_config.pretty_exceptions_enable 

76 ): 

77 _original_except_hook(exc_type, exc_value, tb) 1iceafgbdh

78 return 1iceafgbdh

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

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

81 supress_internal_dir_names = [typer_path, click_path] 1iceafgbdh

82 exc = exc_value 1iceafgbdh

83 if rich: 1iceafgbdh

84 from .rich_utils import MAX_WIDTH 1iceafgbdh

85 

86 rich_tb = Traceback.from_exception( 1iceafgbdh

87 type(exc), 

88 exc, 

89 exc.__traceback__, 

90 show_locals=exception_config.pretty_exceptions_show_locals, 

91 suppress=supress_internal_dir_names, 

92 width=MAX_WIDTH, 

93 ) 

94 console_stderr.print(rich_tb) 1iceafgbdh

95 return 1iceafgbdh

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

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

98 for frame in tb_exc.stack: 1iceafgbdh

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

100 if not exception_config.pretty_exceptions_short: 1iceafgbdh

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

102 stack.append( 1iceafgbdh

103 traceback.FrameSummary( 

104 filename=frame.filename, 

105 lineno=frame.lineno, 

106 name=frame.name, 

107 line="", 

108 ) 

109 ) 

110 else: 

111 stack.append(frame) 1iceafgbdh

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

113 final_stack_summary = StackSummary.from_list(stack) 1iceafgbdh

114 tb_exc.stack = final_stack_summary 1iceafgbdh

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

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

117 return 1iceafgbdh

118 

119 

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

121 install_param, show_param = get_completion_inspect_parameters() 1iceafgbdh

122 click_install_param, _ = get_click_param(install_param) 1iceafgbdh

123 click_show_param, _ = get_click_param(show_param) 1iceafgbdh

124 return click_install_param, click_show_param 1iceafgbdh

125 

126 

127class Typer: 1iceafgbdh

128 def __init__( 1ceafgbdh

129 self, 

130 *, 

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

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

133 invoke_without_command: bool = Default(False), 

134 no_args_is_help: bool = Default(False), 

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

136 chain: bool = Default(False), 

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

138 # Command 

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

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

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

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

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

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

145 add_help_option: bool = Default(True), 

146 hidden: bool = Default(False), 

147 deprecated: bool = Default(False), 

148 add_completion: bool = True, 

149 # Rich settings 

150 rich_markup_mode: MarkupMode = Default(DEFAULT_MARKUP_MODE), 

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

152 pretty_exceptions_enable: bool = True, 

153 pretty_exceptions_show_locals: bool = True, 

154 pretty_exceptions_short: bool = True, 

155 ): 

156 self._add_completion = add_completion 1iceafgbdh

157 self.rich_markup_mode: MarkupMode = rich_markup_mode 1iceafgbdh

158 self.rich_help_panel = rich_help_panel 1iceafgbdh

159 self.pretty_exceptions_enable = pretty_exceptions_enable 1iceafgbdh

160 self.pretty_exceptions_show_locals = pretty_exceptions_show_locals 1iceafgbdh

161 self.pretty_exceptions_short = pretty_exceptions_short 1iceafgbdh

162 self.info = TyperInfo( 1iceafgbdh

163 name=name, 

164 cls=cls, 

165 invoke_without_command=invoke_without_command, 

166 no_args_is_help=no_args_is_help, 

167 subcommand_metavar=subcommand_metavar, 

168 chain=chain, 

169 result_callback=result_callback, 

170 context_settings=context_settings, 

171 callback=callback, 

172 help=help, 

173 epilog=epilog, 

174 short_help=short_help, 

175 options_metavar=options_metavar, 

176 add_help_option=add_help_option, 

177 hidden=hidden, 

178 deprecated=deprecated, 

179 ) 

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

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

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

183 

184 def callback( 1ceafgbdh

185 self, 

186 *, 

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

188 invoke_without_command: bool = Default(False), 

189 no_args_is_help: bool = Default(False), 

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

191 chain: bool = Default(False), 

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

193 # Command 

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

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

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

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

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

199 add_help_option: bool = Default(True), 

200 hidden: bool = Default(False), 

201 deprecated: bool = Default(False), 

202 # Rich settings 

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

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

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

206 self.registered_callback = TyperInfo( 1iceafgbdh

207 cls=cls, 

208 invoke_without_command=invoke_without_command, 

209 no_args_is_help=no_args_is_help, 

210 subcommand_metavar=subcommand_metavar, 

211 chain=chain, 

212 result_callback=result_callback, 

213 context_settings=context_settings, 

214 callback=f, 

215 help=help, 

216 epilog=epilog, 

217 short_help=short_help, 

218 options_metavar=options_metavar, 

219 add_help_option=add_help_option, 

220 hidden=hidden, 

221 deprecated=deprecated, 

222 rich_help_panel=rich_help_panel, 

223 ) 

224 return f 1iceafgbdh

225 

226 return decorator 1iceafgbdh

227 

228 def command( 1ceafgbdh

229 self, 

230 name: Optional[str] = None, 

231 *, 

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

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

234 help: Optional[str] = None, 

235 epilog: Optional[str] = None, 

236 short_help: Optional[str] = None, 

237 options_metavar: str = "[OPTIONS]", 

238 add_help_option: bool = True, 

239 no_args_is_help: bool = False, 

240 hidden: bool = False, 

241 deprecated: bool = False, 

242 # Rich settings 

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

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

245 if cls is None: 1iceafgbdh

246 cls = TyperCommand 1iceafgbdh

247 

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

249 self.registered_commands.append( 1iceafgbdh

250 CommandInfo( 

251 name=name, 

252 cls=cls, 

253 context_settings=context_settings, 

254 callback=f, 

255 help=help, 

256 epilog=epilog, 

257 short_help=short_help, 

258 options_metavar=options_metavar, 

259 add_help_option=add_help_option, 

260 no_args_is_help=no_args_is_help, 

261 hidden=hidden, 

262 deprecated=deprecated, 

263 # Rich settings 

264 rich_help_panel=rich_help_panel, 

265 ) 

266 ) 

267 return f 1iceafgbdh

268 

269 return decorator 1iceafgbdh

270 

271 def add_typer( 1ceafgbdh

272 self, 

273 typer_instance: "Typer", 

274 *, 

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

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

277 invoke_without_command: bool = Default(False), 

278 no_args_is_help: bool = Default(False), 

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

280 chain: bool = Default(False), 

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

282 # Command 

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

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

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

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

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

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

289 add_help_option: bool = Default(True), 

290 hidden: bool = Default(False), 

291 deprecated: bool = Default(False), 

292 # Rich settings 

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

294 ) -> None: 

295 self.registered_groups.append( 1iceafgbdh

296 TyperInfo( 

297 typer_instance, 

298 name=name, 

299 cls=cls, 

300 invoke_without_command=invoke_without_command, 

301 no_args_is_help=no_args_is_help, 

302 subcommand_metavar=subcommand_metavar, 

303 chain=chain, 

304 result_callback=result_callback, 

305 context_settings=context_settings, 

306 callback=callback, 

307 help=help, 

308 epilog=epilog, 

309 short_help=short_help, 

310 options_metavar=options_metavar, 

311 add_help_option=add_help_option, 

312 hidden=hidden, 

313 deprecated=deprecated, 

314 rich_help_panel=rich_help_panel, 

315 ) 

316 ) 

317 

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

319 if sys.excepthook != except_hook: 1iceafgbdh

320 sys.excepthook = except_hook 1iceafgbdh

321 try: 1iceafgbdh

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

323 except Exception as e: 1iceafgbdh

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

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

326 # raise custom_exc from e 

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

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

329 # actual error last. 

330 setattr( 1iceafgbdh

331 e, 

332 _typer_developer_exception_attr_name, 

333 DeveloperExceptionConfig( 

334 pretty_exceptions_enable=self.pretty_exceptions_enable, 

335 pretty_exceptions_show_locals=self.pretty_exceptions_show_locals, 

336 pretty_exceptions_short=self.pretty_exceptions_short, 

337 ), 

338 ) 

339 raise e 1iceafgbdh

340 

341 

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

343 group = get_group_from_info( 1iceafgbdh

344 TyperInfo(typer_instance), 

345 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 

346 rich_markup_mode=typer_instance.rich_markup_mode, 

347 ) 

348 return group 1iceafgbdh

349 

350 

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

352 if typer_instance._add_completion: 1iceafgbdh

353 click_install_param, click_show_param = get_install_completion_arguments() 1iceafgbdh

354 if ( 1cabd

355 typer_instance.registered_callback 

356 or typer_instance.info.callback 

357 or typer_instance.registered_groups 

358 or len(typer_instance.registered_commands) > 1 

359 ): 

360 # Create a Group 

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

362 if typer_instance._add_completion: 1iceafgbdh

363 click_command.params.append(click_install_param) 1iceafgbdh

364 click_command.params.append(click_show_param) 1iceafgbdh

365 return click_command 1iceafgbdh

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

367 # Create a single Command 

368 single_command = typer_instance.registered_commands[0] 1iceafgbdh

369 

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

371 typer_instance.info.context_settings, DefaultPlaceholder 

372 ): 

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

374 

375 click_command = get_command_from_info( 1iceafgbdh

376 single_command, 

377 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 

378 rich_markup_mode=typer_instance.rich_markup_mode, 

379 ) 

380 if typer_instance._add_completion: 1iceafgbdh

381 click_command.params.append(click_install_param) 1iceafgbdh

382 click_command.params.append(click_show_param) 1iceafgbdh

383 return click_command 1iceafgbdh

384 raise RuntimeError( 

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

386 ) # pragma: no cover 

387 

388 

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

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

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

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

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

394 try: 1iceafgbdh

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

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

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

398 except AttributeError: 1iceafgbdh

399 pass 1iceafgbdh

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

401 try: 1iceafgbdh

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

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

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

405 except AttributeError: 1iceafgbdh

406 pass 1iceafgbdh

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

408 if typer_info.callback: 1iceafgbdh

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

410 if doc: 1iceafgbdh

411 return doc 1iceafgbdh

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

413 try: 1iceafgbdh

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

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

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

417 if doc: 1iceafgbdh

418 return doc 1iceafgbdh

419 except AttributeError: 1iceafgbdh

420 pass 1iceafgbdh

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

422 try: 1iceafgbdh

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

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

425 doc = inspect.getdoc(instance_callback) 1iceafgbdh

426 if doc: 1iceafgbdh

427 return doc 1iceafgbdh

428 except AttributeError: 1iceafgbdh

429 pass 1iceafgbdh

430 # Value not set, use the default 

431 return typer_info.help.value 1iceafgbdh

432 

433 

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

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

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

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

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

439 values[name] = value 1iceafgbdh

440 continue 1iceafgbdh

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

442 try: 1iceafgbdh

443 callback_value = getattr( 1iceafgbdh

444 typer_info.typer_instance.registered_callback, # type: ignore 

445 name, 

446 ) 

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

448 values[name] = callback_value 1iceafgbdh

449 continue 1iceafgbdh

450 except AttributeError: 1iceafgbdh

451 pass 1iceafgbdh

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

453 try: 1iceafgbdh

454 instance_value = getattr( 1iceafgbdh

455 typer_info.typer_instance.info, # type: ignore 

456 name, 

457 ) 

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

459 values[name] = instance_value 1iceafgbdh

460 continue 1iceafgbdh

461 except AttributeError: 1iceafgbdh

462 pass 1iceafgbdh

463 # Value not set, use the default 

464 values[name] = value.value 1iceafgbdh

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

466 return TyperInfo(**values) 1iceafgbdh

467 

468 

469def get_group_from_info( 1ceafgbdh

470 group_info: TyperInfo, 

471 *, 

472 pretty_exceptions_short: bool, 

473 rich_markup_mode: MarkupMode, 

474) -> TyperGroup: 

475 assert group_info.typer_instance, ( 1iceafgbdh

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

477 ) 

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

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

480 command = get_command_from_info( 1iceafgbdh

481 command_info=command_info, 

482 pretty_exceptions_short=pretty_exceptions_short, 

483 rich_markup_mode=rich_markup_mode, 

484 ) 

485 if command.name: 1iceafgbdh

486 commands[command.name] = command 1iceafgbdh

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

488 sub_group = get_group_from_info( 1iceafgbdh

489 sub_group_info, 

490 pretty_exceptions_short=pretty_exceptions_short, 

491 rich_markup_mode=rich_markup_mode, 

492 ) 

493 if sub_group.name: 1iceafgbdh

494 commands[sub_group.name] = sub_group 1iceafgbdh

495 else: 

496 if sub_group.callback: 1iceafgbdh

497 import warnings 1iceafgbdh

498 

499 warnings.warn( 1iceafgbdh

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

501 stacklevel=5, 

502 ) 

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

504 commands[sub_command_name] = sub_command 1iceafgbdh

505 solved_info = solve_typer_info_defaults(group_info) 1iceafgbdh

506 ( 1ceafgbdh

507 params, 

508 convertors, 

509 context_param_name, 

510 ) = get_params_convertors_ctx_param_name_from_function(solved_info.callback) 

511 cls = solved_info.cls or TyperGroup 1iceafgbdh

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

513 group = cls( 1iceafgbdh

514 name=solved_info.name or "", 

515 commands=commands, 

516 invoke_without_command=solved_info.invoke_without_command, 

517 no_args_is_help=solved_info.no_args_is_help, 

518 subcommand_metavar=solved_info.subcommand_metavar, 

519 chain=solved_info.chain, 

520 result_callback=solved_info.result_callback, 

521 context_settings=solved_info.context_settings, 

522 callback=get_callback( 

523 callback=solved_info.callback, 

524 params=params, 

525 convertors=convertors, 

526 context_param_name=context_param_name, 

527 pretty_exceptions_short=pretty_exceptions_short, 

528 ), 

529 params=params, 

530 help=solved_info.help, 

531 epilog=solved_info.epilog, 

532 short_help=solved_info.short_help, 

533 options_metavar=solved_info.options_metavar, 

534 add_help_option=solved_info.add_help_option, 

535 hidden=solved_info.hidden, 

536 deprecated=solved_info.deprecated, 

537 rich_markup_mode=rich_markup_mode, 

538 # Rich settings 

539 rich_help_panel=solved_info.rich_help_panel, 

540 ) 

541 return group 1iceafgbdh

542 

543 

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

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

546 

547 

548def get_params_convertors_ctx_param_name_from_function( 1ceafgbdh

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

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

551 params = [] 1iceafgbdh

552 convertors = {} 1iceafgbdh

553 context_param_name = None 1iceafgbdh

554 if callback: 1iceafgbdh

555 parameters = get_params_from_function(callback) 1iceafgbdh

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

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

558 context_param_name = param_name 1iceafgbdh

559 continue 1iceafgbdh

560 click_param, convertor = get_click_param(param) 1iceafgbdh

561 if convertor: 1iceafgbdh

562 convertors[param_name] = convertor 1iceafgbdh

563 params.append(click_param) 1iceafgbdh

564 return params, convertors, context_param_name 1iceafgbdh

565 

566 

567def get_command_from_info( 1ceafgbdh

568 command_info: CommandInfo, 

569 *, 

570 pretty_exceptions_short: bool, 

571 rich_markup_mode: MarkupMode, 

572) -> click.Command: 

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

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

575 use_help = command_info.help 1iceafgbdh

576 if use_help is None: 1iceafgbdh

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

578 else: 

579 use_help = inspect.cleandoc(use_help) 1iceafgbdh

580 ( 1ceafgbdh

581 params, 

582 convertors, 

583 context_param_name, 

584 ) = get_params_convertors_ctx_param_name_from_function(command_info.callback) 

585 cls = command_info.cls or TyperCommand 1iceafgbdh

586 command = cls( 1iceafgbdh

587 name=name, 

588 context_settings=command_info.context_settings, 

589 callback=get_callback( 

590 callback=command_info.callback, 

591 params=params, 

592 convertors=convertors, 

593 context_param_name=context_param_name, 

594 pretty_exceptions_short=pretty_exceptions_short, 

595 ), 

596 params=params, # type: ignore 

597 help=use_help, 

598 epilog=command_info.epilog, 

599 short_help=command_info.short_help, 

600 options_metavar=command_info.options_metavar, 

601 add_help_option=command_info.add_help_option, 

602 no_args_is_help=command_info.no_args_is_help, 

603 hidden=command_info.hidden, 

604 deprecated=command_info.deprecated, 

605 rich_markup_mode=rich_markup_mode, 

606 # Rich settings 

607 rich_help_panel=command_info.rich_help_panel, 

608 ) 

609 return command 1iceafgbdh

610 

611 

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

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

614 if lenient_issubclass(type_, Path): 1iceafgbdh

615 convertor = param_path_convertor 1iceafgbdh

616 if lenient_issubclass(type_, Enum): 1iceafgbdh

617 convertor = generate_enum_convertor(type_) 1iceafgbdh

618 return convertor 1iceafgbdh

619 

620 

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

622 if value is not None: 1iceafgbdh

623 return Path(value) 1iceafgbdh

624 return None 1iceafgbdh

625 

626 

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

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

629 

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

631 if value is not None: 1iceafgbdh

632 val = str(value) 1iceafgbdh

633 if val in val_map: 1iceafgbdh

634 key = val_map[val] 1iceafgbdh

635 return enum(key) 1iceafgbdh

636 

637 return convertor 1iceafgbdh

638 

639 

640def generate_list_convertor( 1ceafgbdh

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

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

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

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

645 return None 1iceafgbdh

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

647 

648 return internal_convertor 1iceafgbdh

649 

650 

651def generate_tuple_convertor( 1ceafgbdh

652 types: Sequence[Any], 

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

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

655 

656 def internal_convertor( 1ceafgbdh

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

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

659 if param_args is None: 1iceafgbdh

660 return None 1iceafgbdh

661 return tuple( 1iceafgbdh

662 convertor(arg) if convertor else arg 

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

664 ) 

665 

666 return internal_convertor 1iceafgbdh

667 

668 

669def get_callback( 1ceafgbdh

670 *, 

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

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

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

674 context_param_name: Optional[str] = None, 

675 pretty_exceptions_short: bool, 

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

677 use_convertors = convertors or {} 1iceafgbdh

678 if not callback: 1iceafgbdh

679 return None 1iceafgbdh

680 parameters = get_params_from_function(callback) 1iceafgbdh

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

682 for param_name in parameters: 1iceafgbdh

683 use_params[param_name] = None 1iceafgbdh

684 for param in params: 1iceafgbdh

685 if param.name: 1iceafgbdh

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

687 

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

689 _rich_traceback_guard = pretty_exceptions_short # noqa: F841 1iceafgbdh

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

691 if k in use_convertors: 1iceafgbdh

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

693 else: 

694 use_params[k] = v 1iceafgbdh

695 if context_param_name: 1iceafgbdh

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

697 return callback(**use_params) 1iceafgbdh

698 

699 update_wrapper(wrapper, callback) 1iceafgbdh

700 return wrapper 1iceafgbdh

701 

702 

703def get_click_type( 1ceafgbdh

704 *, annotation: Any, parameter_info: ParameterInfo 

705) -> click.ParamType: 

706 if parameter_info.click_type is not None: 1iceafgbdh

707 return parameter_info.click_type 1iceafgbdh

708 

709 elif parameter_info.parser is not None: 1iceafgbdh

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

711 

712 elif annotation is str: 1iceafgbdh

713 return click.STRING 1iceafgbdh

714 elif annotation is int: 1iceafgbdh

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

716 min_ = None 1iceafgbdh

717 max_ = None 1iceafgbdh

718 if parameter_info.min is not None: 1iceafgbdh

719 min_ = int(parameter_info.min) 1iceafgbdh

720 if parameter_info.max is not None: 1iceafgbdh

721 max_ = int(parameter_info.max) 1iceafgbdh

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

723 else: 

724 return click.INT 1iceafgbdh

725 elif annotation is float: 1iceafgbdh

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

727 return click.FloatRange( 1iceafgbdh

728 min=parameter_info.min, 

729 max=parameter_info.max, 

730 clamp=parameter_info.clamp, 

731 ) 

732 else: 

733 return click.FLOAT 1iceafgbdh

734 elif annotation is bool: 1iceafgbdh

735 return click.BOOL 1iceafgbdh

736 elif annotation == UUID: 1iceafgbdh

737 return click.UUID 1iceafgbdh

738 elif annotation == datetime: 1iceafgbdh

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

740 elif ( 1ab

741 annotation == Path 

742 or parameter_info.allow_dash 

743 or parameter_info.path_type 

744 or parameter_info.resolve_path 

745 ): 

746 return click.Path( 1iceafgbdh

747 exists=parameter_info.exists, 

748 file_okay=parameter_info.file_okay, 

749 dir_okay=parameter_info.dir_okay, 

750 writable=parameter_info.writable, 

751 readable=parameter_info.readable, 

752 resolve_path=parameter_info.resolve_path, 

753 allow_dash=parameter_info.allow_dash, 

754 path_type=parameter_info.path_type, 

755 ) 

756 elif lenient_issubclass(annotation, FileTextWrite): 1iceafgbdh

757 return click.File( 1iceafgbdh

758 mode=parameter_info.mode or "w", 

759 encoding=parameter_info.encoding, 

760 errors=parameter_info.errors, 

761 lazy=parameter_info.lazy, 

762 atomic=parameter_info.atomic, 

763 ) 

764 elif lenient_issubclass(annotation, FileText): 1iceafgbdh

765 return click.File( 1iceafgbdh

766 mode=parameter_info.mode or "r", 

767 encoding=parameter_info.encoding, 

768 errors=parameter_info.errors, 

769 lazy=parameter_info.lazy, 

770 atomic=parameter_info.atomic, 

771 ) 

772 elif lenient_issubclass(annotation, FileBinaryRead): 1iceafgbdh

773 return click.File( 1iceafgbdh

774 mode=parameter_info.mode or "rb", 

775 encoding=parameter_info.encoding, 

776 errors=parameter_info.errors, 

777 lazy=parameter_info.lazy, 

778 atomic=parameter_info.atomic, 

779 ) 

780 elif lenient_issubclass(annotation, FileBinaryWrite): 1iceafgbdh

781 return click.File( 1iceafgbdh

782 mode=parameter_info.mode or "wb", 

783 encoding=parameter_info.encoding, 

784 errors=parameter_info.errors, 

785 lazy=parameter_info.lazy, 

786 atomic=parameter_info.atomic, 

787 ) 

788 elif lenient_issubclass(annotation, Enum): 1iceafgbdh

789 return click.Choice( 1iceafgbdh

790 [item.value for item in annotation], 

791 case_sensitive=parameter_info.case_sensitive, 

792 ) 

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

794 

795 

796def lenient_issubclass( 1ceafgbdh

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

798) -> bool: 

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

800 

801 

802def get_click_param( 1ceafgbdh

803 param: ParamMeta, 

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

805 # First, find out what will be: 

806 # * ParamInfo (ArgumentInfo or OptionInfo) 

807 # * default_value 

808 # * required 

809 default_value = None 1iceafgbdh

810 required = False 1iceafgbdh

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

812 parameter_info = param.default 1iceafgbdh

813 if parameter_info.default == Required: 1iceafgbdh

814 required = True 1iceafgbdh

815 else: 

816 default_value = parameter_info.default 1iceafgbdh

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

818 required = True 1iceafgbdh

819 parameter_info = ArgumentInfo() 1iceafgbdh

820 else: 

821 default_value = param.default 1iceafgbdh

822 parameter_info = OptionInfo() 1iceafgbdh

823 annotation: Any 

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

825 annotation = param.annotation 1iceafgbdh

826 else: 

827 annotation = str 1iceafgbdh

828 main_type = annotation 1iceafgbdh

829 is_list = False 1iceafgbdh

830 is_tuple = False 1iceafgbdh

831 parameter_type: Any = None 1iceafgbdh

832 is_flag = None 1iceafgbdh

833 origin = get_origin(main_type) 1iceafgbdh

834 

835 if origin is not None: 1iceafgbdh

836 # Handle SomeType | None and Optional[SomeType] 

837 if is_union(origin): 1iceafgbdh

838 types = [] 1iceafgbdh

839 for type_ in get_args(main_type): 1iceafgbdh

840 if type_ is NoneType: 1iceafgbdh

841 continue 1iceafgbdh

842 types.append(type_) 1iceafgbdh

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

844 main_type = types[0] 1iceafgbdh

845 origin = get_origin(main_type) 1iceafgbdh

846 # Handle Tuples and Lists 

847 if lenient_issubclass(origin, List): 1iceafgbdh

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

849 assert not get_origin(main_type), ( 1iceafgbdh

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

851 ) 

852 is_list = True 1iceafgbdh

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

854 types = [] 1iceafgbdh

855 for type_ in get_args(main_type): 1iceafgbdh

856 assert not get_origin(type_), ( 1iceafgbdh

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

858 ) 

859 types.append( 1iceafgbdh

860 get_click_type(annotation=type_, parameter_info=parameter_info) 

861 ) 

862 parameter_type = tuple(types) 1iceafgbdh

863 is_tuple = True 1iceafgbdh

864 if parameter_type is None: 1iceafgbdh

865 parameter_type = get_click_type( 1iceafgbdh

866 annotation=main_type, parameter_info=parameter_info 

867 ) 

868 convertor = determine_type_convertor(main_type) 1iceafgbdh

869 if is_list: 1iceafgbdh

870 convertor = generate_list_convertor( 1iceafgbdh

871 convertor=convertor, default_value=default_value 

872 ) 

873 if is_tuple: 1iceafgbdh

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

875 if isinstance(parameter_info, OptionInfo): 1iceafgbdh

876 if main_type is bool: 1iceafgbdh

877 is_flag = True 1iceafgbdh

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

879 # to bool internally 

880 parameter_type = None 1iceafgbdh

881 default_option_name = get_command_name(param.name) 1iceafgbdh

882 if is_flag: 1iceafgbdh

883 default_option_declaration = ( 1ceafgbdh

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

885 ) 

886 else: 

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

888 param_decls = [param.name] 1iceafgbdh

889 if parameter_info.param_decls: 1iceafgbdh

890 param_decls.extend(parameter_info.param_decls) 1iceafgbdh

891 else: 

892 param_decls.append(default_option_declaration) 1iceafgbdh

893 return ( 1ceafgbdh

894 TyperOption( 

895 # Option 

896 param_decls=param_decls, 

897 show_default=parameter_info.show_default, 

898 prompt=parameter_info.prompt, 

899 confirmation_prompt=parameter_info.confirmation_prompt, 

900 prompt_required=parameter_info.prompt_required, 

901 hide_input=parameter_info.hide_input, 

902 is_flag=is_flag, 

903 multiple=is_list, 

904 count=parameter_info.count, 

905 allow_from_autoenv=parameter_info.allow_from_autoenv, 

906 type=parameter_type, 

907 help=parameter_info.help, 

908 hidden=parameter_info.hidden, 

909 show_choices=parameter_info.show_choices, 

910 show_envvar=parameter_info.show_envvar, 

911 # Parameter 

912 required=required, 

913 default=default_value, 

914 callback=get_param_callback( 

915 callback=parameter_info.callback, convertor=convertor 

916 ), 

917 metavar=parameter_info.metavar, 

918 expose_value=parameter_info.expose_value, 

919 is_eager=parameter_info.is_eager, 

920 envvar=parameter_info.envvar, 

921 shell_complete=parameter_info.shell_complete, 

922 autocompletion=get_param_completion(parameter_info.autocompletion), 

923 # Rich settings 

924 rich_help_panel=parameter_info.rich_help_panel, 

925 ), 

926 convertor, 

927 ) 

928 elif isinstance(parameter_info, ArgumentInfo): 1iceafgbdh

929 param_decls = [param.name] 1iceafgbdh

930 nargs = None 1iceafgbdh

931 if is_list: 1iceafgbdh

932 nargs = -1 1iceafgbdh

933 return ( 1ceafgbdh

934 TyperArgument( 

935 # Argument 

936 param_decls=param_decls, 

937 type=parameter_type, 

938 required=required, 

939 nargs=nargs, 

940 # TyperArgument 

941 show_default=parameter_info.show_default, 

942 show_choices=parameter_info.show_choices, 

943 show_envvar=parameter_info.show_envvar, 

944 help=parameter_info.help, 

945 hidden=parameter_info.hidden, 

946 # Parameter 

947 default=default_value, 

948 callback=get_param_callback( 

949 callback=parameter_info.callback, convertor=convertor 

950 ), 

951 metavar=parameter_info.metavar, 

952 expose_value=parameter_info.expose_value, 

953 is_eager=parameter_info.is_eager, 

954 envvar=parameter_info.envvar, 

955 shell_complete=parameter_info.shell_complete, 

956 autocompletion=get_param_completion(parameter_info.autocompletion), 

957 # Rich settings 

958 rich_help_panel=parameter_info.rich_help_panel, 

959 ), 

960 convertor, 

961 ) 

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

963 

964 

965def get_param_callback( 1ceafgbdh

966 *, 

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

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

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

970 if not callback: 1iceafgbdh

971 return None 1iceafgbdh

972 parameters = get_params_from_function(callback) 1iceafgbdh

973 ctx_name = None 1iceafgbdh

974 click_param_name = None 1iceafgbdh

975 value_name = None 1iceafgbdh

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

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

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

979 ctx_name = param_name 1iceafgbdh

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

981 click_param_name = param_name 1iceafgbdh

982 else: 

983 untyped_names.append(param_name) 1iceafgbdh

984 # Extract value param name first 

985 if untyped_names: 1iceafgbdh

986 value_name = untyped_names.pop() 1iceafgbdh

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

988 if untyped_names: 1iceafgbdh

989 if ctx_name is None: 1iceafgbdh

990 ctx_name = untyped_names.pop(0) 1iceafgbdh

991 if click_param_name is None: 1iceafgbdh

992 if untyped_names: 1iceafgbdh

993 click_param_name = untyped_names.pop(0) 1iceafgbdh

994 if untyped_names: 1iceafgbdh

995 raise click.ClickException( 1iceafgbdh

996 "Too many CLI parameter callback function parameters" 

997 ) 

998 

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

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

1001 if ctx_name: 1iceafgbdh

1002 use_params[ctx_name] = ctx 1iceafgbdh

1003 if click_param_name: 1iceafgbdh

1004 use_params[click_param_name] = param 1iceafgbdh

1005 if value_name: 1iceafgbdh

1006 if convertor: 1iceafgbdh

1007 use_value = convertor(value) 1iceafgbdh

1008 else: 

1009 use_value = value 1iceafgbdh

1010 use_params[value_name] = use_value 1iceafgbdh

1011 return callback(**use_params) 1iceafgbdh

1012 

1013 update_wrapper(wrapper, callback) 1iceafgbdh

1014 return wrapper 1iceafgbdh

1015 

1016 

1017def get_param_completion( 1ceafgbdh

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

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

1020 if not callback: 1iceafgbdh

1021 return None 1iceafgbdh

1022 parameters = get_params_from_function(callback) 1iceafgbdh

1023 ctx_name = None 1iceafgbdh

1024 args_name = None 1iceafgbdh

1025 incomplete_name = None 1iceafgbdh

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

1027 for param_sig in unassigned_params[:]: 1iceafgbdh

1028 origin = get_origin(param_sig.annotation) 1iceafgbdh

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

1030 ctx_name = param_sig.name 1iceafgbdh

1031 unassigned_params.remove(param_sig) 1iceafgbdh

1032 elif lenient_issubclass(origin, List): 1iceafgbdh

1033 args_name = param_sig.name 1iceafgbdh

1034 unassigned_params.remove(param_sig) 1iceafgbdh

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

1036 incomplete_name = param_sig.name 1iceafgbdh

1037 unassigned_params.remove(param_sig) 1iceafgbdh

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

1039 for param_sig in unassigned_params[:]: 1iceafgbdh

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

1041 ctx_name = param_sig.name 1iceafgbdh

1042 unassigned_params.remove(param_sig) 1iceafgbdh

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

1044 args_name = param_sig.name 1iceafgbdh

1045 unassigned_params.remove(param_sig) 1iceafgbdh

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

1047 incomplete_name = param_sig.name 1iceafgbdh

1048 unassigned_params.remove(param_sig) 1iceafgbdh

1049 # Extract value param name first 

1050 if unassigned_params: 1iceafgbdh

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

1052 raise click.ClickException( 1iceafgbdh

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

1054 ) 

1055 

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

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

1058 if ctx_name: 1iceafgbdh

1059 use_params[ctx_name] = ctx 1iceafgbdh

1060 if args_name: 1iceafgbdh

1061 use_params[args_name] = args 1iceafgbdh

1062 if incomplete_name: 1iceafgbdh

1063 use_params[incomplete_name] = incomplete 1iceafgbdh

1064 return callback(**use_params) 1iceafgbdh

1065 

1066 update_wrapper(wrapper, callback) 1iceafgbdh

1067 return wrapper 1iceafgbdh

1068 

1069 

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

1071 app = Typer(add_completion=False) 1iceafgbdh

1072 app.command()(function) 1iceafgbdh

1073 app() 1iceafgbdh

1074 

1075 

1076def _is_macos() -> bool: 1iceafgbdh

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

1078 

1079 

1080def _is_linux_or_bsd() -> bool: 1iceafgbdh

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

1082 return True 1iceafgbdh

1083 

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

1085 

1086 

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

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

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

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

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

1092 success. 

1093 

1094 This function handles url in different operating systems separately: 

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

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

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

1098 

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

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

1101 

1102 Examples:: 

1103 

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

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

1106 

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

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

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

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

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

1112 application associated with the URL it will attempt to 

1113 launch a file manager with the file located. This 

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

1115 the filesystem. 

1116 """ 

1117 

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

1119 if _is_macos(): 1iceafgbdh

1120 return subprocess.Popen( 1iceafgbdh

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

1122 ).wait() 

1123 

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

1125 

1126 if has_xdg_open: 1iceafgbdh

1127 return subprocess.Popen( 1iceafgbdh

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

1129 ).wait() 

1130 

1131 import webbrowser 1iceafgbdh

1132 

1133 webbrowser.open(url) 1iceafgbdh

1134 

1135 return 0 1iceafgbdh

1136 

1137 else: 

1138 return click.launch(url) 1iceafgbdh