Coverage for typer/main.py: 100%

500 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-11-13 11:07 +0000

1import inspect 1hceafbdg

2import os 1hceafbdg

3import platform 1hceafbdg

4import shutil 1hceafbdg

5import subprocess 1hceafbdg

6import sys 1hceafbdg

7import traceback 1hceafbdg

8from datetime import datetime 1hceafbdg

9from enum import Enum 1hceafbdg

10from functools import update_wrapper 1hceafbdg

11from pathlib import Path 1hceafbdg

12from traceback import FrameSummary, StackSummary 1hceafbdg

13from types import TracebackType 1hceafbdg

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

15from uuid import UUID 1hceafbdg

16 

17import click 1hceafbdg

18from typing_extensions import get_args, get_origin 1hceafbdg

19 

20from ._typing import is_union 1hceafbdg

21from .completion import get_completion_inspect_parameters 1hceafbdg

22from .core import ( 1hceafbdg

23 DEFAULT_MARKUP_MODE, 

24 MarkupMode, 

25 TyperArgument, 

26 TyperCommand, 

27 TyperGroup, 

28 TyperOption, 

29) 

30from .models import ( 1hceafbdg

31 AnyType, 

32 ArgumentInfo, 

33 CommandFunctionType, 

34 CommandInfo, 

35 Default, 

36 DefaultPlaceholder, 

37 DeveloperExceptionConfig, 

38 FileBinaryRead, 

39 FileBinaryWrite, 

40 FileText, 

41 FileTextWrite, 

42 NoneType, 

43 OptionInfo, 

44 ParameterInfo, 

45 ParamMeta, 

46 Required, 

47 TyperInfo, 

48) 

49from .utils import get_params_from_function 1hceafbdg

50 

51try: 1hceafbdg

52 import rich 1hceafbdg

53 from rich.traceback import Traceback 1hceafbdg

54 

55 from . import rich_utils 1hceafbdg

56 

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

58 

59except ImportError: # pragma: no cover 

60 rich = None # type: ignore 

61 

62_original_except_hook = sys.excepthook 1hceafbdg

63_typer_developer_exception_attr_name = "__typer_developer_exception__" 1hceafbdg

64 

65 

66def except_hook( 1ceafbdg

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

68) -> None: 

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

70 exc_value, _typer_developer_exception_attr_name, None 

71 ) 

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

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) 1hceafbdg

79 return 1hceafbdg

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

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

82 supress_internal_dir_names = [typer_path, click_path] 1hceafbdg

83 exc = exc_value 1hceafbdg

84 if rich: 1hceafbdg

85 from .rich_utils import MAX_WIDTH 1hceafbdg

86 

87 rich_tb = Traceback.from_exception( 1hceafbdg

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) 1hceafbdg

96 return 1hceafbdg

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

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

99 for frame in tb_exc.stack: 1hceafbdg

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

101 if not exception_config.pretty_exceptions_short: 1hceafbdg

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

103 stack.append( 1hceafbdg

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) 1hceafbdg

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

114 final_stack_summary = StackSummary.from_list(stack) 1hceafbdg

115 tb_exc.stack = final_stack_summary 1hceafbdg

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

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

118 return 1hceafbdg

119 

120 

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

122 install_param, show_param = get_completion_inspect_parameters() 1hceafbdg

123 click_install_param, _ = get_click_param(install_param) 1hceafbdg

124 click_show_param, _ = get_click_param(show_param) 1hceafbdg

125 return click_install_param, click_show_param 1hceafbdg

126 

127 

128class Typer: 1hceafbdg

129 def __init__( 1ceafbdg

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 1hceafbdg

158 self.rich_markup_mode: MarkupMode = rich_markup_mode 1hceafbdg

159 self.rich_help_panel = rich_help_panel 1hceafbdg

160 self.pretty_exceptions_enable = pretty_exceptions_enable 1hceafbdg

161 self.pretty_exceptions_show_locals = pretty_exceptions_show_locals 1hceafbdg

162 self.pretty_exceptions_short = pretty_exceptions_short 1hceafbdg

163 self.info = TyperInfo( 1hceafbdg

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] = [] 1hceafbdg

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

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

184 

185 def callback( 1ceafbdg

186 self, 

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

188 *, 

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

190 invoke_without_command: bool = Default(False), 

191 no_args_is_help: bool = Default(False), 

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

193 chain: bool = Default(False), 

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

195 # Command 

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

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

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

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

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

201 add_help_option: bool = Default(True), 

202 hidden: bool = Default(False), 

203 deprecated: bool = Default(False), 

204 # Rich settings 

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

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

207 def decorator(f: CommandFunctionType) -> CommandFunctionType: 1hceafbdg

208 self.registered_callback = TyperInfo( 1hceafbdg

209 name=name, 

210 cls=cls, 

211 invoke_without_command=invoke_without_command, 

212 no_args_is_help=no_args_is_help, 

213 subcommand_metavar=subcommand_metavar, 

214 chain=chain, 

215 result_callback=result_callback, 

216 context_settings=context_settings, 

217 callback=f, 

218 help=help, 

219 epilog=epilog, 

220 short_help=short_help, 

221 options_metavar=options_metavar, 

222 add_help_option=add_help_option, 

223 hidden=hidden, 

224 deprecated=deprecated, 

225 rich_help_panel=rich_help_panel, 

226 ) 

227 return f 1hceafbdg

228 

229 return decorator 1hceafbdg

230 

231 def command( 1ceafbdg

232 self, 

233 name: Optional[str] = None, 

234 *, 

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

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

237 help: Optional[str] = None, 

238 epilog: Optional[str] = None, 

239 short_help: Optional[str] = None, 

240 options_metavar: str = "[OPTIONS]", 

241 add_help_option: bool = True, 

242 no_args_is_help: bool = False, 

243 hidden: bool = False, 

244 deprecated: bool = False, 

245 # Rich settings 

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

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

248 if cls is None: 1hceafbdg

249 cls = TyperCommand 1hceafbdg

250 

251 def decorator(f: CommandFunctionType) -> CommandFunctionType: 1hceafbdg

252 self.registered_commands.append( 1hceafbdg

253 CommandInfo( 

254 name=name, 

255 cls=cls, 

256 context_settings=context_settings, 

257 callback=f, 

258 help=help, 

259 epilog=epilog, 

260 short_help=short_help, 

261 options_metavar=options_metavar, 

262 add_help_option=add_help_option, 

263 no_args_is_help=no_args_is_help, 

264 hidden=hidden, 

265 deprecated=deprecated, 

266 # Rich settings 

267 rich_help_panel=rich_help_panel, 

268 ) 

269 ) 

270 return f 1hceafbdg

271 

272 return decorator 1hceafbdg

273 

274 def add_typer( 1ceafbdg

275 self, 

276 typer_instance: "Typer", 

277 *, 

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

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

280 invoke_without_command: bool = Default(False), 

281 no_args_is_help: bool = Default(False), 

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

283 chain: bool = Default(False), 

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

285 # Command 

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

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

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

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

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

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

292 add_help_option: bool = Default(True), 

293 hidden: bool = Default(False), 

294 deprecated: bool = Default(False), 

295 # Rich settings 

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

297 ) -> None: 

298 self.registered_groups.append( 1hceafbdg

299 TyperInfo( 

300 typer_instance, 

301 name=name, 

302 cls=cls, 

303 invoke_without_command=invoke_without_command, 

304 no_args_is_help=no_args_is_help, 

305 subcommand_metavar=subcommand_metavar, 

306 chain=chain, 

307 result_callback=result_callback, 

308 context_settings=context_settings, 

309 callback=callback, 

310 help=help, 

311 epilog=epilog, 

312 short_help=short_help, 

313 options_metavar=options_metavar, 

314 add_help_option=add_help_option, 

315 hidden=hidden, 

316 deprecated=deprecated, 

317 rich_help_panel=rich_help_panel, 

318 ) 

319 ) 

320 

321 def __call__(self, *args: Any, **kwargs: Any) -> Any: 1hceafbdg

322 if sys.excepthook != except_hook: 1hceafbdg

323 sys.excepthook = except_hook 1hceafbdg

324 try: 1hceafbdg

325 return get_command(self)(*args, **kwargs) 1hceafbdg

326 except Exception as e: 1hceafbdg

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

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

329 # raise custom_exc from e 

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

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

332 # actual error last. 

333 setattr( 1hceafbdg

334 e, 

335 _typer_developer_exception_attr_name, 

336 DeveloperExceptionConfig( 

337 pretty_exceptions_enable=self.pretty_exceptions_enable, 

338 pretty_exceptions_show_locals=self.pretty_exceptions_show_locals, 

339 pretty_exceptions_short=self.pretty_exceptions_short, 

340 ), 

341 ) 

342 raise e 1hceafbdg

343 

344 

345def get_group(typer_instance: Typer) -> TyperGroup: 1hceafbdg

346 group = get_group_from_info( 1hceafbdg

347 TyperInfo(typer_instance), 

348 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 

349 rich_markup_mode=typer_instance.rich_markup_mode, 

350 ) 

351 return group 1hceafbdg

352 

353 

354def get_command(typer_instance: Typer) -> click.Command: 1hceafbdg

355 if typer_instance._add_completion: 1hceafbdg

356 click_install_param, click_show_param = get_install_completion_arguments() 1hceafbdg

357 if ( 1cabd

358 typer_instance.registered_callback 

359 or typer_instance.info.callback 

360 or typer_instance.registered_groups 

361 or len(typer_instance.registered_commands) > 1 

362 ): 

363 # Create a Group 

364 click_command: click.Command = get_group(typer_instance) 1hceafbdg

365 if typer_instance._add_completion: 1hceafbdg

366 click_command.params.append(click_install_param) 1hceafbdg

367 click_command.params.append(click_show_param) 1hceafbdg

368 return click_command 1hceafbdg

369 elif len(typer_instance.registered_commands) == 1: 1hceafbdg

370 # Create a single Command 

371 single_command = typer_instance.registered_commands[0] 1hceafbdg

372 

373 if not single_command.context_settings and not isinstance( 1hceafbdg

374 typer_instance.info.context_settings, DefaultPlaceholder 

375 ): 

376 single_command.context_settings = typer_instance.info.context_settings 1hceafbdg

377 

378 click_command = get_command_from_info( 1hceafbdg

379 single_command, 

380 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 

381 rich_markup_mode=typer_instance.rich_markup_mode, 

382 ) 

383 if typer_instance._add_completion: 1hceafbdg

384 click_command.params.append(click_install_param) 1hceafbdg

385 click_command.params.append(click_show_param) 1hceafbdg

386 return click_command 1hceafbdg

387 raise RuntimeError( 

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

389 ) # pragma: no cover 

390 

391 

392def get_group_name(typer_info: TyperInfo) -> Optional[str]: 1hceafbdg

393 if typer_info.callback: 1hceafbdg

394 # Priority 1: Callback passed in app.add_typer() 

395 return get_command_name(typer_info.callback.__name__) 1hceafbdg

396 if typer_info.typer_instance: 1hceafbdg

397 registered_callback = typer_info.typer_instance.registered_callback 1hceafbdg

398 if registered_callback: 1hceafbdg

399 if registered_callback.callback: 1hceafbdg

400 # Priority 2: Callback passed in @subapp.callback() 

401 return get_command_name(registered_callback.callback.__name__) 1hceafbdg

402 if typer_info.typer_instance.info.callback: 1hceafbdg

403 return get_command_name(typer_info.typer_instance.info.callback.__name__) 1hceafbdg

404 return None 1hceafbdg

405 

406 

407def solve_typer_info_help(typer_info: TyperInfo) -> str: 1hceafbdg

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

409 if not isinstance(typer_info.help, DefaultPlaceholder): 1hceafbdg

410 return inspect.cleandoc(typer_info.help or "") 1hceafbdg

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

412 try: 1hceafbdg

413 callback_help = typer_info.typer_instance.registered_callback.help 1hceafbdg

414 if not isinstance(callback_help, DefaultPlaceholder): 1hceafbdg

415 return inspect.cleandoc(callback_help or "") 1hceafbdg

416 except AttributeError: 1hceafbdg

417 pass 1hceafbdg

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

419 try: 1hceafbdg

420 instance_help = typer_info.typer_instance.info.help 1hceafbdg

421 if not isinstance(instance_help, DefaultPlaceholder): 1hceafbdg

422 return inspect.cleandoc(instance_help or "") 1hceafbdg

423 except AttributeError: 1hceafbdg

424 pass 1hceafbdg

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

426 if typer_info.callback: 1hceafbdg

427 doc = inspect.getdoc(typer_info.callback) 1hceafbdg

428 if doc: 1hceafbdg

429 return doc 1hceafbdg

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

431 try: 1hceafbdg

432 callback = typer_info.typer_instance.registered_callback.callback 1hceafbdg

433 if not isinstance(callback, DefaultPlaceholder): 1hceafbdg

434 doc = inspect.getdoc(callback or "") 1hceafbdg

435 if doc: 1hceafbdg

436 return doc 1hceafbdg

437 except AttributeError: 1hceafbdg

438 pass 1hceafbdg

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

440 try: 1hceafbdg

441 instance_callback = typer_info.typer_instance.info.callback 1hceafbdg

442 if not isinstance(instance_callback, DefaultPlaceholder): 1hceafbdg

443 doc = inspect.getdoc(instance_callback) 1hceafbdg

444 if doc: 1hceafbdg

445 return doc 1hceafbdg

446 except AttributeError: 1hceafbdg

447 pass 1hceafbdg

448 # Value not set, use the default 

449 return typer_info.help.value 1hceafbdg

450 

451 

452def solve_typer_info_defaults(typer_info: TyperInfo) -> TyperInfo: 1hceafbdg

453 values: Dict[str, Any] = {} 1hceafbdg

454 for name, value in typer_info.__dict__.items(): 1hceafbdg

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

456 if not isinstance(value, DefaultPlaceholder): 1hceafbdg

457 values[name] = value 1hceafbdg

458 continue 1hceafbdg

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

460 try: 1hceafbdg

461 callback_value = getattr( 1hceafbdg

462 typer_info.typer_instance.registered_callback, # type: ignore 

463 name, 

464 ) 

465 if not isinstance(callback_value, DefaultPlaceholder): 1hceafbdg

466 values[name] = callback_value 1hceafbdg

467 continue 1hceafbdg

468 except AttributeError: 1hceafbdg

469 pass 1hceafbdg

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

471 try: 1hceafbdg

472 instance_value = getattr( 1hceafbdg

473 typer_info.typer_instance.info, # type: ignore 

474 name, 

475 ) 

476 if not isinstance(instance_value, DefaultPlaceholder): 1hceafbdg

477 values[name] = instance_value 1hceafbdg

478 continue 1hceafbdg

479 except AttributeError: 1hceafbdg

480 pass 1hceafbdg

481 # Value not set, use the default 

482 values[name] = value.value 1hceafbdg

483 if values["name"] is None: 1hceafbdg

484 values["name"] = get_group_name(typer_info) 1hceafbdg

485 values["help"] = solve_typer_info_help(typer_info) 1hceafbdg

486 return TyperInfo(**values) 1hceafbdg

487 

488 

489def get_group_from_info( 1ceafbdg

490 group_info: TyperInfo, 

491 *, 

492 pretty_exceptions_short: bool, 

493 rich_markup_mode: MarkupMode, 

494) -> TyperGroup: 

495 assert ( 1cabd

496 group_info.typer_instance 

497 ), "A Typer instance is needed to generate a Click Group" 

498 commands: Dict[str, click.Command] = {} 1hceafbdg

499 for command_info in group_info.typer_instance.registered_commands: 1hceafbdg

500 command = get_command_from_info( 1hceafbdg

501 command_info=command_info, 

502 pretty_exceptions_short=pretty_exceptions_short, 

503 rich_markup_mode=rich_markup_mode, 

504 ) 

505 if command.name: 1hceafbdg

506 commands[command.name] = command 1hceafbdg

507 for sub_group_info in group_info.typer_instance.registered_groups: 1hceafbdg

508 sub_group = get_group_from_info( 1hceafbdg

509 sub_group_info, 

510 pretty_exceptions_short=pretty_exceptions_short, 

511 rich_markup_mode=rich_markup_mode, 

512 ) 

513 if sub_group.name: 1hceafbdg

514 commands[sub_group.name] = sub_group 1hceafbdg

515 solved_info = solve_typer_info_defaults(group_info) 1hceafbdg

516 ( 1ceafbdg

517 params, 

518 convertors, 

519 context_param_name, 

520 ) = get_params_convertors_ctx_param_name_from_function(solved_info.callback) 

521 cls = solved_info.cls or TyperGroup 1hceafbdg

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

523 group = cls( 1hceafbdg

524 name=solved_info.name or "", 

525 commands=commands, 

526 invoke_without_command=solved_info.invoke_without_command, 

527 no_args_is_help=solved_info.no_args_is_help, 

528 subcommand_metavar=solved_info.subcommand_metavar, 

529 chain=solved_info.chain, 

530 result_callback=solved_info.result_callback, 

531 context_settings=solved_info.context_settings, 

532 callback=get_callback( 

533 callback=solved_info.callback, 

534 params=params, 

535 convertors=convertors, 

536 context_param_name=context_param_name, 

537 pretty_exceptions_short=pretty_exceptions_short, 

538 ), 

539 params=params, 

540 help=solved_info.help, 

541 epilog=solved_info.epilog, 

542 short_help=solved_info.short_help, 

543 options_metavar=solved_info.options_metavar, 

544 add_help_option=solved_info.add_help_option, 

545 hidden=solved_info.hidden, 

546 deprecated=solved_info.deprecated, 

547 rich_markup_mode=rich_markup_mode, 

548 # Rich settings 

549 rich_help_panel=solved_info.rich_help_panel, 

550 ) 

551 return group 1hceafbdg

552 

553 

554def get_command_name(name: str) -> str: 1hceafbdg

555 return name.lower().replace("_", "-") 1hceafbdg

556 

557 

558def get_params_convertors_ctx_param_name_from_function( 1ceafbdg

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

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

561 params = [] 1hceafbdg

562 convertors = {} 1hceafbdg

563 context_param_name = None 1hceafbdg

564 if callback: 1hceafbdg

565 parameters = get_params_from_function(callback) 1hceafbdg

566 for param_name, param in parameters.items(): 1hceafbdg

567 if lenient_issubclass(param.annotation, click.Context): 1hceafbdg

568 context_param_name = param_name 1hceafbdg

569 continue 1hceafbdg

570 click_param, convertor = get_click_param(param) 1hceafbdg

571 if convertor: 1hceafbdg

572 convertors[param_name] = convertor 1hceafbdg

573 params.append(click_param) 1hceafbdg

574 return params, convertors, context_param_name 1hceafbdg

575 

576 

577def get_command_from_info( 1ceafbdg

578 command_info: CommandInfo, 

579 *, 

580 pretty_exceptions_short: bool, 

581 rich_markup_mode: MarkupMode, 

582) -> click.Command: 

583 assert command_info.callback, "A command must have a callback function" 1hceafbdg

584 name = command_info.name or get_command_name(command_info.callback.__name__) 1hceafbdg

585 use_help = command_info.help 1hceafbdg

586 if use_help is None: 1hceafbdg

587 use_help = inspect.getdoc(command_info.callback) 1hceafbdg

588 else: 

589 use_help = inspect.cleandoc(use_help) 1hceafbdg

590 ( 1ceafbdg

591 params, 

592 convertors, 

593 context_param_name, 

594 ) = get_params_convertors_ctx_param_name_from_function(command_info.callback) 

595 cls = command_info.cls or TyperCommand 1hceafbdg

596 command = cls( 1hceafbdg

597 name=name, 

598 context_settings=command_info.context_settings, 

599 callback=get_callback( 

600 callback=command_info.callback, 

601 params=params, 

602 convertors=convertors, 

603 context_param_name=context_param_name, 

604 pretty_exceptions_short=pretty_exceptions_short, 

605 ), 

606 params=params, # type: ignore 

607 help=use_help, 

608 epilog=command_info.epilog, 

609 short_help=command_info.short_help, 

610 options_metavar=command_info.options_metavar, 

611 add_help_option=command_info.add_help_option, 

612 no_args_is_help=command_info.no_args_is_help, 

613 hidden=command_info.hidden, 

614 deprecated=command_info.deprecated, 

615 rich_markup_mode=rich_markup_mode, 

616 # Rich settings 

617 rich_help_panel=command_info.rich_help_panel, 

618 ) 

619 return command 1hceafbdg

620 

621 

622def determine_type_convertor(type_: Any) -> Optional[Callable[[Any], Any]]: 1hceafbdg

623 convertor: Optional[Callable[[Any], Any]] = None 1hceafbdg

624 if lenient_issubclass(type_, Path): 1hceafbdg

625 convertor = param_path_convertor 1hceafbdg

626 if lenient_issubclass(type_, Enum): 1hceafbdg

627 convertor = generate_enum_convertor(type_) 1hceafbdg

628 return convertor 1hceafbdg

629 

630 

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

632 if value is not None: 1hceafbdg

633 return Path(value) 1hceafbdg

634 return None 1hceafbdg

635 

636 

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

638 val_map = {str(val.value): val for val in enum} 1hceafbdg

639 

640 def convertor(value: Any) -> Any: 1hceafbdg

641 if value is not None: 1hceafbdg

642 val = str(value) 1hceafbdg

643 if val in val_map: 1hceafbdg

644 key = val_map[val] 1hceafbdg

645 return enum(key) 1hceafbdg

646 

647 return convertor 1hceafbdg

648 

649 

650def generate_list_convertor( 1ceafbdg

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

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

653 def internal_convertor(value: Sequence[Any]) -> Optional[List[Any]]: 1hceafbdg

654 if default_value is None and len(value) == 0: 1hceafbdg

655 return None 1hceafbdg

656 return [convertor(v) if convertor else v for v in value] 1hceafbdg

657 

658 return internal_convertor 1hceafbdg

659 

660 

661def generate_tuple_convertor( 1ceafbdg

662 types: Sequence[Any], 

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

664 convertors = [determine_type_convertor(type_) for type_ in types] 1hceafbdg

665 

666 def internal_convertor( 1ceafbdg

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

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

669 if param_args is None: 1hceafbdg

670 return None 1hceafbdg

671 return tuple( 1hceafbdg

672 convertor(arg) if convertor else arg 

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

674 ) 

675 

676 return internal_convertor 1hceafbdg

677 

678 

679def get_callback( 1ceafbdg

680 *, 

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

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

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

684 context_param_name: Optional[str] = None, 

685 pretty_exceptions_short: bool, 

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

687 use_convertors = convertors or {} 1hceafbdg

688 if not callback: 1hceafbdg

689 return None 1hceafbdg

690 parameters = get_params_from_function(callback) 1hceafbdg

691 use_params: Dict[str, Any] = {} 1hceafbdg

692 for param_name in parameters: 1hceafbdg

693 use_params[param_name] = None 1hceafbdg

694 for param in params: 1hceafbdg

695 if param.name: 1hceafbdg

696 use_params[param.name] = param.default 1hceafbdg

697 

698 def wrapper(**kwargs: Any) -> Any: 1hceafbdg

699 _rich_traceback_guard = pretty_exceptions_short # noqa: F841 1hceafbdg

700 for k, v in kwargs.items(): 1hceafbdg

701 if k in use_convertors: 1hceafbdg

702 use_params[k] = use_convertors[k](v) 1hceafbdg

703 else: 

704 use_params[k] = v 1hceafbdg

705 if context_param_name: 1hceafbdg

706 use_params[context_param_name] = click.get_current_context() 1hceafbdg

707 return callback(**use_params) 1hceafbdg

708 

709 update_wrapper(wrapper, callback) 1hceafbdg

710 return wrapper 1hceafbdg

711 

712 

713def get_click_type( 1ceafbdg

714 *, annotation: Any, parameter_info: ParameterInfo 

715) -> click.ParamType: 

716 if parameter_info.click_type is not None: 1hceafbdg

717 return parameter_info.click_type 1hceafbdg

718 

719 elif parameter_info.parser is not None: 1hceafbdg

720 return click.types.FuncParamType(parameter_info.parser) 1hceafbdg

721 

722 elif annotation is str: 1hceafbdg

723 return click.STRING 1hceafbdg

724 elif annotation is int: 1hceafbdg

725 if parameter_info.min is not None or parameter_info.max is not None: 1hceafbdg

726 min_ = None 1hceafbdg

727 max_ = None 1hceafbdg

728 if parameter_info.min is not None: 1hceafbdg

729 min_ = int(parameter_info.min) 1hceafbdg

730 if parameter_info.max is not None: 1hceafbdg

731 max_ = int(parameter_info.max) 1hceafbdg

732 return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp) 1hceafbdg

733 else: 

734 return click.INT 1hceafbdg

735 elif annotation is float: 1hceafbdg

736 if parameter_info.min is not None or parameter_info.max is not None: 1hceafbdg

737 return click.FloatRange( 1hceafbdg

738 min=parameter_info.min, 

739 max=parameter_info.max, 

740 clamp=parameter_info.clamp, 

741 ) 

742 else: 

743 return click.FLOAT 1hceafbdg

744 elif annotation is bool: 1hceafbdg

745 return click.BOOL 1hceafbdg

746 elif annotation == UUID: 1hceafbdg

747 return click.UUID 1hceafbdg

748 elif annotation == datetime: 1hceafbdg

749 return click.DateTime(formats=parameter_info.formats) 1hceafbdg

750 elif ( 1ab

751 annotation == Path 

752 or parameter_info.allow_dash 

753 or parameter_info.path_type 

754 or parameter_info.resolve_path 

755 ): 

756 return click.Path( 1hceafbdg

757 exists=parameter_info.exists, 

758 file_okay=parameter_info.file_okay, 

759 dir_okay=parameter_info.dir_okay, 

760 writable=parameter_info.writable, 

761 readable=parameter_info.readable, 

762 resolve_path=parameter_info.resolve_path, 

763 allow_dash=parameter_info.allow_dash, 

764 path_type=parameter_info.path_type, 

765 ) 

766 elif lenient_issubclass(annotation, FileTextWrite): 1hceafbdg

767 return click.File( 1hceafbdg

768 mode=parameter_info.mode or "w", 

769 encoding=parameter_info.encoding, 

770 errors=parameter_info.errors, 

771 lazy=parameter_info.lazy, 

772 atomic=parameter_info.atomic, 

773 ) 

774 elif lenient_issubclass(annotation, FileText): 1hceafbdg

775 return click.File( 1hceafbdg

776 mode=parameter_info.mode or "r", 

777 encoding=parameter_info.encoding, 

778 errors=parameter_info.errors, 

779 lazy=parameter_info.lazy, 

780 atomic=parameter_info.atomic, 

781 ) 

782 elif lenient_issubclass(annotation, FileBinaryRead): 1hceafbdg

783 return click.File( 1hceafbdg

784 mode=parameter_info.mode or "rb", 

785 encoding=parameter_info.encoding, 

786 errors=parameter_info.errors, 

787 lazy=parameter_info.lazy, 

788 atomic=parameter_info.atomic, 

789 ) 

790 elif lenient_issubclass(annotation, FileBinaryWrite): 1hceafbdg

791 return click.File( 1hceafbdg

792 mode=parameter_info.mode or "wb", 

793 encoding=parameter_info.encoding, 

794 errors=parameter_info.errors, 

795 lazy=parameter_info.lazy, 

796 atomic=parameter_info.atomic, 

797 ) 

798 elif lenient_issubclass(annotation, Enum): 1hceafbdg

799 return click.Choice( 1hceafbdg

800 [item.value for item in annotation], 

801 case_sensitive=parameter_info.case_sensitive, 

802 ) 

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

804 

805 

806def lenient_issubclass( 1ceafbdg

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

808) -> bool: 

809 return isinstance(cls, type) and issubclass(cls, class_or_tuple) 1hceafbdg

810 

811 

812def get_click_param( 1ceafbdg

813 param: ParamMeta, 

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

815 # First, find out what will be: 

816 # * ParamInfo (ArgumentInfo or OptionInfo) 

817 # * default_value 

818 # * required 

819 default_value = None 1hceafbdg

820 required = False 1hceafbdg

821 if isinstance(param.default, ParameterInfo): 1hceafbdg

822 parameter_info = param.default 1hceafbdg

823 if parameter_info.default == Required: 1hceafbdg

824 required = True 1hceafbdg

825 else: 

826 default_value = parameter_info.default 1hceafbdg

827 elif param.default == Required or param.default is param.empty: 1hceafbdg

828 required = True 1hceafbdg

829 parameter_info = ArgumentInfo() 1hceafbdg

830 else: 

831 default_value = param.default 1hceafbdg

832 parameter_info = OptionInfo() 1hceafbdg

833 annotation: Any 

834 if param.annotation is not param.empty: 1hceafbdg

835 annotation = param.annotation 1hceafbdg

836 else: 

837 annotation = str 1hceafbdg

838 main_type = annotation 1hceafbdg

839 is_list = False 1hceafbdg

840 is_tuple = False 1hceafbdg

841 parameter_type: Any = None 1hceafbdg

842 is_flag = None 1hceafbdg

843 origin = get_origin(main_type) 1hceafbdg

844 

845 if origin is not None: 1hceafbdg

846 # Handle SomeType | None and Optional[SomeType] 

847 if is_union(origin): 1hceafbdg

848 types = [] 1hceafbdg

849 for type_ in get_args(main_type): 1hceafbdg

850 if type_ is NoneType: 1hceafbdg

851 continue 1hceafbdg

852 types.append(type_) 1hceafbdg

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

854 main_type = types[0] 1hceafbdg

855 origin = get_origin(main_type) 1hceafbdg

856 # Handle Tuples and Lists 

857 if lenient_issubclass(origin, List): 1hceafbdg

858 main_type = get_args(main_type)[0] 1hceafbdg

859 assert not get_origin( 1hceafbdg

860 main_type 

861 ), "List types with complex sub-types are not currently supported" 

862 is_list = True 1hceafbdg

863 elif lenient_issubclass(origin, Tuple): # type: ignore 1hceafbdg

864 types = [] 1hceafbdg

865 for type_ in get_args(main_type): 1hceafbdg

866 assert not get_origin( 1hceafbdg

867 type_ 

868 ), "Tuple types with complex sub-types are not currently supported" 

869 types.append( 1hceafbdg

870 get_click_type(annotation=type_, parameter_info=parameter_info) 

871 ) 

872 parameter_type = tuple(types) 1hceafbdg

873 is_tuple = True 1hceafbdg

874 if parameter_type is None: 1hceafbdg

875 parameter_type = get_click_type( 1hceafbdg

876 annotation=main_type, parameter_info=parameter_info 

877 ) 

878 convertor = determine_type_convertor(main_type) 1hceafbdg

879 if is_list: 1hceafbdg

880 convertor = generate_list_convertor( 1hceafbdg

881 convertor=convertor, default_value=default_value 

882 ) 

883 if is_tuple: 1hceafbdg

884 convertor = generate_tuple_convertor(get_args(main_type)) 1hceafbdg

885 if isinstance(parameter_info, OptionInfo): 1hceafbdg

886 if main_type is bool: 1hceafbdg

887 is_flag = True 1hceafbdg

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

889 # to bool internally 

890 parameter_type = None 1hceafbdg

891 default_option_name = get_command_name(param.name) 1hceafbdg

892 if is_flag: 1hceafbdg

893 default_option_declaration = ( 1ceafbdg

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

895 ) 

896 else: 

897 default_option_declaration = f"--{default_option_name}" 1hceafbdg

898 param_decls = [param.name] 1hceafbdg

899 if parameter_info.param_decls: 1hceafbdg

900 param_decls.extend(parameter_info.param_decls) 1hceafbdg

901 else: 

902 param_decls.append(default_option_declaration) 1hceafbdg

903 return ( 1ceafbdg

904 TyperOption( 

905 # Option 

906 param_decls=param_decls, 

907 show_default=parameter_info.show_default, 

908 prompt=parameter_info.prompt, 

909 confirmation_prompt=parameter_info.confirmation_prompt, 

910 prompt_required=parameter_info.prompt_required, 

911 hide_input=parameter_info.hide_input, 

912 is_flag=is_flag, 

913 multiple=is_list, 

914 count=parameter_info.count, 

915 allow_from_autoenv=parameter_info.allow_from_autoenv, 

916 type=parameter_type, 

917 help=parameter_info.help, 

918 hidden=parameter_info.hidden, 

919 show_choices=parameter_info.show_choices, 

920 show_envvar=parameter_info.show_envvar, 

921 # Parameter 

922 required=required, 

923 default=default_value, 

924 callback=get_param_callback( 

925 callback=parameter_info.callback, convertor=convertor 

926 ), 

927 metavar=parameter_info.metavar, 

928 expose_value=parameter_info.expose_value, 

929 is_eager=parameter_info.is_eager, 

930 envvar=parameter_info.envvar, 

931 shell_complete=parameter_info.shell_complete, 

932 autocompletion=get_param_completion(parameter_info.autocompletion), 

933 # Rich settings 

934 rich_help_panel=parameter_info.rich_help_panel, 

935 ), 

936 convertor, 

937 ) 

938 elif isinstance(parameter_info, ArgumentInfo): 1hceafbdg

939 param_decls = [param.name] 1hceafbdg

940 nargs = None 1hceafbdg

941 if is_list: 1hceafbdg

942 nargs = -1 1hceafbdg

943 return ( 1ceafbdg

944 TyperArgument( 

945 # Argument 

946 param_decls=param_decls, 

947 type=parameter_type, 

948 required=required, 

949 nargs=nargs, 

950 # TyperArgument 

951 show_default=parameter_info.show_default, 

952 show_choices=parameter_info.show_choices, 

953 show_envvar=parameter_info.show_envvar, 

954 help=parameter_info.help, 

955 hidden=parameter_info.hidden, 

956 # Parameter 

957 default=default_value, 

958 callback=get_param_callback( 

959 callback=parameter_info.callback, convertor=convertor 

960 ), 

961 metavar=parameter_info.metavar, 

962 expose_value=parameter_info.expose_value, 

963 is_eager=parameter_info.is_eager, 

964 envvar=parameter_info.envvar, 

965 shell_complete=parameter_info.shell_complete, 

966 autocompletion=get_param_completion(parameter_info.autocompletion), 

967 # Rich settings 

968 rich_help_panel=parameter_info.rich_help_panel, 

969 ), 

970 convertor, 

971 ) 

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

973 

974 

975def get_param_callback( 1ceafbdg

976 *, 

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

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

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

980 if not callback: 1hceafbdg

981 return None 1hceafbdg

982 parameters = get_params_from_function(callback) 1hceafbdg

983 ctx_name = None 1hceafbdg

984 click_param_name = None 1hceafbdg

985 value_name = None 1hceafbdg

986 untyped_names: List[str] = [] 1hceafbdg

987 for param_name, param_sig in parameters.items(): 1hceafbdg

988 if lenient_issubclass(param_sig.annotation, click.Context): 1hceafbdg

989 ctx_name = param_name 1hceafbdg

990 elif lenient_issubclass(param_sig.annotation, click.Parameter): 1hceafbdg

991 click_param_name = param_name 1hceafbdg

992 else: 

993 untyped_names.append(param_name) 1hceafbdg

994 # Extract value param name first 

995 if untyped_names: 1hceafbdg

996 value_name = untyped_names.pop() 1hceafbdg

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

998 if untyped_names: 1hceafbdg

999 if ctx_name is None: 1hceafbdg

1000 ctx_name = untyped_names.pop(0) 1hceafbdg

1001 if click_param_name is None: 1hceafbdg

1002 if untyped_names: 1hceafbdg

1003 click_param_name = untyped_names.pop(0) 1hceafbdg

1004 if untyped_names: 1hceafbdg

1005 raise click.ClickException( 1hceafbdg

1006 "Too many CLI parameter callback function parameters" 

1007 ) 

1008 

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

1010 use_params: Dict[str, Any] = {} 1hceafbdg

1011 if ctx_name: 1hceafbdg

1012 use_params[ctx_name] = ctx 1hceafbdg

1013 if click_param_name: 1hceafbdg

1014 use_params[click_param_name] = param 1hceafbdg

1015 if value_name: 1hceafbdg

1016 if convertor: 1hceafbdg

1017 use_value = convertor(value) 1hceafbdg

1018 else: 

1019 use_value = value 1hceafbdg

1020 use_params[value_name] = use_value 1hceafbdg

1021 return callback(**use_params) 1hceafbdg

1022 

1023 update_wrapper(wrapper, callback) 1hceafbdg

1024 return wrapper 1hceafbdg

1025 

1026 

1027def get_param_completion( 1ceafbdg

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

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

1030 if not callback: 1hceafbdg

1031 return None 1hceafbdg

1032 parameters = get_params_from_function(callback) 1hceafbdg

1033 ctx_name = None 1hceafbdg

1034 args_name = None 1hceafbdg

1035 incomplete_name = None 1hceafbdg

1036 unassigned_params = list(parameters.values()) 1hceafbdg

1037 for param_sig in unassigned_params[:]: 1hceafbdg

1038 origin = get_origin(param_sig.annotation) 1hceafbdg

1039 if lenient_issubclass(param_sig.annotation, click.Context): 1hceafbdg

1040 ctx_name = param_sig.name 1hceafbdg

1041 unassigned_params.remove(param_sig) 1hceafbdg

1042 elif lenient_issubclass(origin, List): 1hceafbdg

1043 args_name = param_sig.name 1hceafbdg

1044 unassigned_params.remove(param_sig) 1hceafbdg

1045 elif lenient_issubclass(param_sig.annotation, str): 1hceafbdg

1046 incomplete_name = param_sig.name 1hceafbdg

1047 unassigned_params.remove(param_sig) 1hceafbdg

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

1049 for param_sig in unassigned_params[:]: 1hceafbdg

1050 if ctx_name is None and param_sig.name == "ctx": 1hceafbdg

1051 ctx_name = param_sig.name 1hceafbdg

1052 unassigned_params.remove(param_sig) 1hceafbdg

1053 elif args_name is None and param_sig.name == "args": 1hceafbdg

1054 args_name = param_sig.name 1hceafbdg

1055 unassigned_params.remove(param_sig) 1hceafbdg

1056 elif incomplete_name is None and param_sig.name == "incomplete": 1hceafbdg

1057 incomplete_name = param_sig.name 1hceafbdg

1058 unassigned_params.remove(param_sig) 1hceafbdg

1059 # Extract value param name first 

1060 if unassigned_params: 1hceafbdg

1061 show_params = " ".join([param.name for param in unassigned_params]) 1hceafbdg

1062 raise click.ClickException( 1hceafbdg

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

1064 ) 

1065 

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

1067 use_params: Dict[str, Any] = {} 1hceafbdg

1068 if ctx_name: 1hceafbdg

1069 use_params[ctx_name] = ctx 1hceafbdg

1070 if args_name: 1hceafbdg

1071 use_params[args_name] = args 1hceafbdg

1072 if incomplete_name: 1hceafbdg

1073 use_params[incomplete_name] = incomplete 1hceafbdg

1074 return callback(**use_params) 1hceafbdg

1075 

1076 update_wrapper(wrapper, callback) 1hceafbdg

1077 return wrapper 1hceafbdg

1078 

1079 

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

1081 app = Typer(add_completion=False) 1hceafbdg

1082 app.command()(function) 1hceafbdg

1083 app() 1hceafbdg

1084 

1085 

1086def _is_macos() -> bool: 1hceafbdg

1087 return platform.system() == "Darwin" 1hceafbdg

1088 

1089 

1090def _is_linux_or_bsd() -> bool: 1hceafbdg

1091 if platform.system() == "Linux": 1hceafbdg

1092 return True 1hceafbdg

1093 

1094 return "BSD" in platform.system() 1hceafbdg

1095 

1096 

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

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

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

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

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

1102 success. 

1103 

1104 This function handles url in different operating systems separately: 

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

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

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

1108 

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

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

1111 

1112 Examples:: 

1113 

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

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

1116 

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

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

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

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

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

1122 application associated with the URL it will attempt to 

1123 launch a file manager with the file located. This 

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

1125 the filesystem. 

1126 """ 

1127 

1128 if url.startswith("http://") or url.startswith("https://"): 1hceafbdg

1129 if _is_macos(): 1hceafbdg

1130 return subprocess.Popen( 1hceafbdg

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

1132 ).wait() 

1133 

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

1135 

1136 if has_xdg_open: 1hceafbdg

1137 return subprocess.Popen( 1hceafbdg

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

1139 ).wait() 

1140 

1141 import webbrowser 1hceafbdg

1142 

1143 webbrowser.open(url) 1hceafbdg

1144 

1145 return 0 1hceafbdg

1146 

1147 else: 

1148 return click.launch(url) 1hceafbdg