def connection( ctx: typer.Context, username: str = typer.Option( ..., prompt=True, envvar="HI_USERNAME", help= 'Username without "@hi.is". If the environment variable does not exist you will be prompted.', ), password: str = typer.Option( ..., prompt=True, hide_input=True, envvar="HI_PASSWORD", help="If the environment variable does not exist you will be prompted.", ), host: SFTPHosts = typer.Option(SFTPHosts.krafla, help="sftp remote hostname"), ): """ Create sftp connection """ sftp, _ = get_connection(host, username, password) ctx.ensure_object(dict) ctx.obj["sftp"] = sftp
def eve_esi( ctx: typer.Context, version: str = typer.Option( "latest", "--version", help="Esi schema version to load from Eve Esi Jobs app data directory", ), schema_path: Optional[Path] = typer.Option( None, "--schema-path", "-s", help="Path to local schema file." ), ): """ Welcome to Eve Esi Jobs. Get started by downloading a schema, or checkout the docs at: https://eve-esi-jobs.readthedocs.io/en/latest/ """ config = make_config_from_env() typer.echo(f"Logging at {config.log_path}") ctx.obj = {} start = perf_counter_ns() ctx.obj["start_time"] = start logger.info("loading schema") ctx.obj["config"] = config schema = None schema_source = "" if schema_path is not None: try: schema_text = schema_path.read_text() schema = json.loads(schema_text) typer.echo(f"Loaded schema from {schema_path}") schema_source = str(schema_path) except FileNotFoundError as ex: logger.exception("Error loading schema from file.") raise typer.BadParameter( f"Error loading schema from {schema_path}. " f"Error: {ex.__class__.__name__} msg: {ex}." ) else: schema = load_schema(config.app_dir, version) schema_source = str(config.app_dir) + f"version: {version}" if schema is None: typer.echo("Schema not found in app data, attempting to download.") typer.echo("Consider using `eve-esi schema download` to save a local copy,") typer.echo("or provide a valid local path to the schema.") schema = download_json(config.schema_url) schema_source = config.schema_url try: operation_manifest = OperationManifest(schema) except Exception as ex: logger.exception( "Tried to make operation_manifest with invalid schema. version: %s, source: %s, error: %s, msg: %s", version, schema_source, ex.__class__.__name__, ex, ) raise typer.BadParameter( "The provided schema was invalid. please try a different one." ) ctx.obj["operation_manifest"] = operation_manifest typer.echo(f"Loaded ESI schema version {operation_manifest.version}.\n")
def openapi_main( ctx: typer.Context, token: str = typer.Option('', envvar='TINVEST_TOKEN'), # noqa:B008 sandbox_token: str = typer.Option('', envvar='TINVEST_SANDBOX_TOKEN'), # noqa:B008 use_sandbox: bool = typer.Option(False, '--use-sandbox', '-s'), # noqa:B008 ): ctx.ensure_object(OpenapiCtx) ctx.obj.token = token ctx.obj.sandbox_token = sandbox_token ctx.obj.use_sandbox = use_sandbox
def copy( ctx: typer.Context, existing_task_spec_name: str, new_task_spec_name=typer.Argument(None), new_title: str = None, destination_dir: Path = None, new_extension: str = None, edit_after_write: bool = False, ): """Copy an old Task Spec to a new one with the provided name""" original_file: Path = task_spec_location(existing_task_spec_name) if original_file is None: warn_missing_file(existing_task_spec_name) return old_task_spec: TaskSpec = deserialize_task_spec_file(original_file) interactive = existing_task_spec_name is not None or new_task_spec_name is not None if interactive: extension_without_dot = original_file.suffix.lstrip(".") defaults = { "default_title": old_task_spec.title, "default_destination": original_file.parent, # TODO use friendly prefix here "default_extension": extension_without_dot, # TODO enum for extensions "edit_after_write": edit_after_write, } ( new_task_spec_name, new_title, destination_dir, new_extension, edit_after_write, ) = prompt_for_copy_args(**defaults) new_filename = f"{new_task_spec_name}.{new_extension}" new_task_spec: TaskSpec = old_task_spec.copy( update={"filename": new_filename, "title": new_title} ) try: writer.write(new_task_spec, destination_dir.expanduser()) except FileExistsError: if confirm_overwrite(new_task_spec_name): writer.write(new_task_spec, destination_dir.expanduser(), force=True) if edit_after_write: ctx.invoke(edit, task_spec_name=new_task_spec_name) success(f"Copy of {existing_task_spec_name} written to {destination_dir.resolve()}")
def new( ctx: typer.Context, task_spec_name: str = typer.Argument(None), destination_dir: Path = config.default_destination_dir, extension: str = "not", expert: bool = False, edit_after_write: bool = config.edit_after_write, overwrite: bool = False, interactive: bool = config.interactive_new, ): """Template out a new Task Spec to the nearest .nothing directory and open with $EDITOR. When no arguments are provided, Task Spec is configured interactively.""" if interactive or task_spec_name is None: defaults = { "default_extension": extension, "default_destination": destination_dir, "expert": expert, "edit_after_write": edit_after_write, } ( task_spec_name, extension, destination_dir, expert, edit_after_write, ) = prompt_for_new_args(**defaults) # XXX how to skip prompt when name is specified? task_spec_filename = f"{task_spec_name}.{extension}" task_spec: TaskSpec = ( TaskSpecCreate(filename=task_spec_filename) if not expert else TaskSpecCreateExpert(filename=task_spec_filename) ) try: writer.write( task_spec, destination_dir.expanduser(), force=interactive or overwrite ) except FileExistsError: if confirm_overwrite(task_spec_name): writer.write(task_spec, destination_dir.expanduser(), force=True) if edit_after_write: ctx.invoke(edit, task_spec_name=task_spec_name) success(f"{task_spec_filename} written to {destination_dir.resolve()}")
def hamming( ctx: typer.Context, verbose: bool = typer.Option(False, "-v", "--verbose", help="Verbose output"), ): """Top-level entrypoint into hamming-codec commandline utilities""" if ctx.invoked_subcommand is None: print("No subcommand specified") sys.exit(1) # ensure that ctx.obj exists and is a dict ctx.ensure_object(dict) # pass the verbose flag to the sub-commands ctx.obj["VERBOSE"] = verbose
def main_callback( ctx: typer.Context, light_id: int = typer.Option( 0, "--light-id", "-l", show_default=True, help="Which light to operate on, see list output.", ), all_lights: bool = typer.Option(False, "--all", "-a", is_flag=True, help="Operate on all lights."), debug: bool = typer.Option( False, "--debug", "-D", is_flag=True, help="Enable logging", ), ): """Control USB attached LED lights like a Human™ Make a USB attached LED light turn on, off and blink; all from the comfort of your very own command-line. If your platform supports HIDAPI (Linux, MacOS, Windows and probably others), then you can use busylight with supported lights! """ ctx.obj = ALL_LIGHTS if all_lights else light_id if debug: logger.enable("busylight")
def setup_config(ctx: typer.Context, config_path: Path): logging.info(f'Using config from {config_path.absolute()}') assert config_path.is_file(), 'asdf' config = loads(config_path.read_text()) ctx.meta['config'] = config return config
def callback( ctx: Context, model: Supported = Option(Supported.default, "--sensor-model", "-m", help="sensor model"), port: str = Option("/dev/ttyUSB0", "--serial-port", "-s", help="serial port"), seconds: int = Option(60, "--interval", "-i", help="seconds to wait between updates"), samples: Optional[int] = Option(None, "--samples", "-n", help="stop after N samples"), debug: bool = Option(False, "--debug", help="print DEBUG/logging messages"), version: Optional[bool] = Option(None, "--version", "-V", callback=version_callback), ): """Read serial sensor""" logger.setLevel("DEBUG" if debug else os.getenv("LEVEL", "WARNING")) ctx.obj = {"reader": SensorReader(model, port, seconds, samples)}
def _parse_commit( ctx: Context, *, input: FileText = Option( "-", help= "A file to read commits from. If `-`, commits will be read from stdin.", ), output: FileText = Option( "-", help= "A file to write parsed commits to. If `-`, parsed commits will be written to stdout.", mode="w", ), include_unparsed: bool = Option( False, help="If set, commits which fail to be parsed will be returned."), ) -> None: """ Parses a stream of commits in the given file or from stdin. """ from asyncio import run from confuse import Configuration from .parse_commit import cli_main config = ctx.find_object(Configuration) run( cli_main( config, input=input, output=output, include_unparsed=include_unparsed, ))
def _list_commits( ctx: Context, *, output: FileText = Option( "-", help= "A file to write commits to. If `-`, commits will be written to stdout.", mode="w", ), from_rev: Optional[str] = Option( None, "--from", help="The commit or tag to start from when listing commits from.", ), from_last_tag: bool = Option( False, "--from-last-tag", help="If given, the commit list will start from the most-recent tag.", ), to_rev: str = Option("HEAD", "--to", help="The commit or tag to stop at listing commits."), reverse: bool = Option( False, "--reverse", help= "If given, the list of commits will be reversed (ie. oldest commit first).", ), parse: bool = Option( False, "--parse", help="If set, commits will be parsed with `parse-commit`."), include_unparsed: bool = Option( False, help= "If set, commits which fail to be parsed will be included in the output. See `parse-commit`.", ), ) -> None: """ Retrieves commits from the git repository at PATH, or the current directory if PATH is not provided. """ from asyncio import run from confuse import Configuration from .list_commits import cli_main config = ctx.find_object(Configuration) run( cli_main( config, output=output, from_rev=from_rev, from_last_tag=from_last_tag, to_rev=to_rev, reverse=reverse, parse=parse, include_unparsed=include_unparsed, ))
def main( ctx: typer.Context, cfg: Path = typer.Option(DEFAULT_PROJECT_CONFIG, "--config", "-c", help="Set the configuration file."), repository: str = typer.Option( DEFAULT_REPOSITORY, help="Set the remote repository. Required format: owner/repository.", ), token: str = typer.Option(DEFAULT_GITHUB_TOKEN, help="Set github access token."), log_level: LoggingChoices = typer.Option(DEFAULT_LOG_LEVEL, help="Set logging level."), ): """ Hammurabi is an extensible CLI tool responsible for enforcing user-defined rules on a git repository. Find more information at: https://hammurabi.readthedocs.io/latest/ """ if ctx.invoked_subcommand in NON_ACTIONABLE_SUBCOMMANDS: return os.environ.setdefault("HAMMURABI_SETTINGS_PATH", str(cfg.expanduser())) try: # Reload the configuration config.load() success_message("Configuration loaded") except Exception as exc: # pylint: disable=broad-except error_message(f"Failed to load configuration: {str(exc)}") return if token != DEFAULT_GITHUB_TOKEN: config.github = github3.login(token=token) if repository != DEFAULT_REPOSITORY: config.settings.repository = repository if log_level != DEFAULT_LOG_LEVEL: logging.root.setLevel(log_level.value) ctx.ensure_object(dict) ctx.obj["config"] = config
def main(ctx: typer.Context, cfg_file: str = None): """Capture traces for side-channel analysis.""" cfg_file = 'capture_aes.yaml' if cfg_file is None else cfg_file with open(cfg_file) as f: cfg = yaml.load(f, Loader=yaml.FullLoader) # Store config in the user data attribute (`obj`) of the context. ctx.obj = SimpleNamespace(cfg=cfg)
def init( ctx: typer.Context, config_file: List[pathlib.Path] = typer.Option( None, exists=True, dir_okay=False, help="A file to read configuration values from. May be specified multiple times to combine configuration values from multiple files.", ), verbosity: Verbosity = typer.Option( Verbosity.info, case_sensitive=False, help="Set the verbosity of the logging." ), ) -> None: """ Conventional - An extensible command-line tool for parsing and processing structured commits. """ import asyncio import logging import confuse from .util.typer import ColorFormatter, TyperHandler handler = TyperHandler() handler.formatter = ColorFormatter() logger = logging.getLogger() logging.basicConfig(handlers=[handler], force=True) # Importing aiocache results in a warning being logged. Temporarily disable it # until it has been imported. logging.getLogger("aiocache").setLevel(logging.ERROR) import aiocache # noqa: F401 # Reset aiocache log level since aiocache has now been imported and unnecessary # warning has been avioded. logging.getLogger("aiocache").setLevel(logging.NOTSET) from .util.config import find_project_configuration_file logging.getLogger().setLevel(getattr(logging, verbosity)) config = confuse.Configuration("Conventional", "conventional") project_config_file = asyncio.run(find_project_configuration_file()) if project_config_file is not None: logger.debug(f"Loading configuration file, {project_config_file.as_posix()}") config.set_file(project_config_file) for filename in config_file: logger.debug(f"Loading configuration file, {filename.as_posix()}") config.set_file(filename) ctx.obj = config
def main( ctx: typer.Context, verbose: bool = typer.Option( False, help="Enable verbose logging to the terminal"), full: bool = typer.Option(False, help="Print all fields from CRUD commands"), raw: bool = typer.Option( False, help="Print output from CRUD commands as raw json"), version: bool = typer.Option( False, help="Print the version of jobbergate-cli and exit"), ): """ Welcome to the Jobbergate CLI! More information can be shown for each command listed below by running it with the --help option. """ if version: typer.echo(importlib_metadata.version("jobbergate-cli")) raise typer.Exit() if ctx.invoked_subcommand is None: terminal_message( conjoin( "No command provided. Please check the [bold magenta]usage[/bold magenta] and add a command", "", f"[yellow]{ctx.get_help()}[/yellow]", ), subject="Need a jobbergate command", ) raise typer.Exit() init_logs(verbose=verbose) init_sentry() persona = None client = httpx.Client( base_url=f"https://{settings.AUTH0_LOGIN_DOMAIN}", headers={"content-type": "application/x-www-form-urlencoded"}, ) context = JobbergateContext(persona=None, client=client) if ctx.invoked_subcommand not in ("login", "logout"): persona = init_persona(context) context.client = httpx.Client( base_url=settings.JOBBERGATE_API_ENDPOINT, headers=dict( Authorization=f"Bearer {persona.token_set.access_token}"), ) context.persona = persona context.full_output = full context.raw_output = raw ctx.obj = context
def send_cmds_node_callback(ctx: typer.Context, commands: Union[str, Tuple[str]]): if ctx.resilient_parsing: # tab completion, return without validating return # utils.json_print(ctx.__dict__) # utils.json_print(ctx.parent.__dict__) # utils.json_print(locals()) if ctx.params["kw1"].lower() == "all" and ctx.params["nodes"].lower( ) == "commands": ctx.params["nodes"] = None return tuple([ctx.params["kw2"], *commands]) else: return commands
def sync(ctx: typer.Context, project: str = typer.Argument( ..., help='The name for the project, specified in config file'), since: datetime = typer.Option(..., formats=['%Y-%m-%d']), until: datetime = typer.Option(..., formats=['%Y-%m-%d']), dry: bool = typer.Option( False, help='Use log entries instead of uploading them to redmine'), drain: bool = typer.Option( False, help='Use drain issues for entries without specified dest')): config = setup_config(ctx, ctx.meta['config_path']) setup_http(ctx) ctx.meta['rdm_user'] = extract.get_redmine_user(config["redmine"]["url"]) time_entries = get_toggl_enteries(config, project, since, until) issues = get_redmine_issues(config, project, since) issue_ids = petl.columns(issues)['id'] entries_to_load, unset_entries = petl.biselect( time_entries, lambda row: row['issue_id'] in issue_ids) if drain and petl.nrows(unset_entries): log.info('Using drain') drained, unset_entries = drained_entries(ctx, issues, unset_entries, project) log.info(f'Drained {petl.nrows(drained)} issues') entries_to_load = petl.cat(entries_to_load, drained) if petl.nrows(unset_entries): log.warning(f'There\'re {petl.nrows(unset_entries)} unset entries') if get_proj_attr(config, project, 'group_entries'): log.info('Using group by day and description') entries_to_load = transform.group_entries_by_day(entries_to_load) load.to_redmine_time(config["redmine"]["url"], entries_to_load, activity_id=get_proj_attr(config, project, 'rdm_activity_id'), user_id=ctx.meta['rdm_user'].get('id'), dry=dry)
def _template( ctx: Context, *, input: Optional[FileText] = Option( None, help= "A file to read commits from. If `-`, commits will be read from stdin. Defaults to reading commits from a git repository in the current directory.", ), output: FileText = Option( "-", help= "A file to write parsed commits to. If `-`, parsed commits will be written to stdout.", mode="w", ), include_unparsed: bool = Option( False, help= "If set, commits which fail to be parsed will be returned. See `parse-commit`.", ), unreleased_version: Optional[str] = Option( None, help="If set, will be used as the tag name for unreleased commits."), template_name: Optional[str] = Option( None, help="If set, will override the name of the template to be loaded."), ) -> None: """ Reads a stream of commits from the given file or stdin and uses them to render a template. """ from asyncio import run from confuse import Configuration from .template import cli_main config = ctx.find_object(Configuration) if template_name is not None: config.set_args({"template.name": template_name}, dots=True) run( cli_main( config, input=input, output=output, include_unparsed=include_unparsed, unreleased_version=unreleased_version, ))
def init( self, ctx: Context, version: Optional[bool] = Option(None, "--version", is_eager=True, help="Show version number and exit"), ): if ctx.resilient_parsing: # pragma: no cover return if version: v = get_distribution("falca").version cprint(f":package: Falca v{v}") raise Exit ctx.obj = self.app
def validate_parameters(ctx: typer.Context, value: List[str]) -> List[str]: file_path = Path(ctx.params["file_path"]).absolute() if not ctx.obj: parsed_src = SourceParser( str(SourceParser.get_project_path(file_path)), str(file_path) ) parsed_src.set_entity(str(parsed_src.get_entities()[0].get_name())) ctx.obj = parsed_src else: parsed_src: SourceParser = ctx.obj parameter_name_list = [p.get_name() for p in parsed_src.get_parameters()] for chosen_parameter in value: if chosen_parameter not in parameter_name_list: raise typer.BadParameter( "Chosen parameter: " + chosen_parameter + " is not among available parameters " + str(parameter_name_list) ) return value
def main( ctx: typer.Context, config_path: Path = typer.Option( get_default_config, resolve_path=True, ), logger_config: Path = typer.Option( get_default_logger_config, resolve_path=True, ), ): ctx.meta['config_path'] = config_path if logger_config.exists(): if logger_config.suffix == '.toml': conf = loads(logger_config.read_text()) logging_configurator.dictConfig(conf) elif logger_config.suffix == '.ini': logging_configurator.fileConfig(str(logger_config)) else: logging.basicConfig(level=INFO) log.info('Using default logger config')
def root( ctx: typer.Context, version: typing.Optional[bool] = typer.Option( False, "--version", callback=version_callback, is_eager=True, ), base_url: str = typer. Option( "http://localhost:8090", help= "Set the base-url of your instance. This flag will overwrite scheme, host, path and port, if those are set in" "this string.", callback=furl_callback, ), user: str = typer.Option( "admin", help="Set the username of the user you want to use", ), password: str = typer.Option( "admin", help="Set the password of the user you want to use", ), port: typing.Optional[int] = typer.Option(None), ask_for_password: typing.Optional[bool] = typer.Option( False, help="Asks user for password interactively"), logo: bool = typer.Option(True, help="Print lively apps logo"), ): """ A simple command line plugin uploader/installer/manager for atlassian product server instances (Confluence/Jira) written in python(3). """ if logo: print(LOGO) if ask_for_password: password = typer.prompt("Password: "******"base_url": burl}
def main( ctx: typer.Context, version: Optional[bool] = typer.Option( None, "--version", help="Show version and exit.", callback=version_callback, is_eager=True, ), overlay_dir: Optional[Path] = typer.Option( ".", "--overlay-dir", help="Specify location for overlay." ), worker_count: int = typer.Option( 8, min=1, help="Number of workers for creating package cache." ), quiet: Optional[bool] = typer.Option( False, "--quiet", help="Suppresses output. For commands checking versions exit code 100 means newer versions are available.", ), ): """Provides certain tools to be run on the overlay directory. See individual commands help for details.""" state = State() ctx.obj = state if overlay_dir != ".": state.overlay_dir = Path(overlay_dir).absolute() state.quiet = quiet or (ctx.invoked_subcommand == "mkreadme") state.print_stdout("Starting overlay-maintain-tools CLI") state.print_stdout(f"Building package cache from {str(overlay_dir)}.") state.pkg_cache = sorted( build_pkgs_cache(overlay_dir=overlay_dir, worker_count=worker_count), key=lambda _: _.atomname, ) state.print_stdout(f"Package cache built.") state.worker_count = worker_count
async def cli(ctx: Context, config: str = typer.Option("aerich.ini", "--config", "-c", help="Config file.", ), app: str = typer.Option(None, help="Tortoise-ORM app name."), name: str = typer.Option("aerich", "--name", "-n", help="Name of section in .ini file to use for aerich config.", )): ctx.ensure_object(dict) ctx.obj["config_file"] = config ctx.obj["name"] = name invoked_subcommand = ctx.invoked_subcommand sys.path.insert(0, ".") if invoked_subcommand != "init": if not os.path.exists(config): typer.secho("You must exec init first", ) raise typer.Exit() parser.read(config) location = parser[name]["location"] tortoise_orm = parser[name]["tortoise_orm"] tortoise_config = get_tortoise_config(ctx, tortoise_orm) app = app or list(tortoise_config.get("apps").keys())[0] if "aerich.models" not in tortoise_config.get("apps").get(app).get("models"): typer.secho("Check your tortoise config and add aerich.models to it.") raise typer.Exit() ctx.obj["config"] = tortoise_config ctx.obj["location"] = location ctx.obj["app"] = app
def reset( ctx: typer.Context, confirm: bool = typer.Option( ..., prompt="You're about to drop the database and re-create it. Continue?", confirmation_prompt=True, ), ): """Executes all "db" commands in sequence. Args: ctx (typer.Context): The command context confirm (bool): User's confirmation for destructive operation Raises: typer.Abort: If the user does not confirm the operation """ if not confirm: raise typer.Abort() ctx.invoke(db_init.db_init, drop=True) ctx.invoke(db_grid.db_grid) ctx.invoke(db_rankings.db_rankings)
def main(context: typer.Context, verbose: bool = False): """Perform various development-oriented tasks for this plugin""" context.obj = {"verbose": verbose}
def blync_callback( ctx: typer.Context, light_id: int = typer.Option( 0, "--light-id", "-l", show_default=True, help="Light identifier", ), red: int = typer.Option( 0, "--red", "-r", is_flag=True, show_default=True, help="Red color value range: 0 - 255", ), blue: int = typer.Option( 0, "--blue", "-b", is_flag=True, show_default=True, help="Blue color value range: 0 - 255", ), green: int = typer.Option( 0, "--green", "-g", is_flag=True, help="Green color value range: 0 - 255", show_default=True, ), red_b: bool = typer.Option(False, "--RED", "-R", is_flag=True, help="Full value red [255]"), blue_b: bool = typer.Option(False, "--BLUE", "-B", is_flag=True, help="Full value blue [255]"), green_b: bool = typer.Option(False, "--GREEN", "-G", is_flag=True, help="Full value green [255]"), off: bool = typer.Option(False, "--off/--on", "-o/-n", show_default=True, help="Turn the light off/on."), dim: bool = typer.Option( False, "--dim", "-d", is_flag=True, help="Toggle bright/dim mode.", show_default=True, ), flash: int = typer.Option( 0, "--flash", "-f", count=True, is_flag=True, help="Enable flash mode.", ), play: int = typer.Option(0, "--play", "-p", help="Select song: 1-15"), repeat: bool = typer.Option( False, "--repeat", is_flag=True, show_default=True, help="Repeat the selected song.", ), volume: int = typer.Option(5, "--volume", show_default=True, help="Set the volume: 1-10"), available: bool = typer.Option( False, "--list-available", "-a", is_flag=True, is_eager=True, callback=list_lights, ), verbose: int = typer.Option(0, "--verbose", "-v", count=True, callback=verbosity), version: bool = typer.Option(False, "--version", "-V", is_flag=True, is_eager=True, callback=report_version), ): """Control your Embrava BlyncLight from the command-line! ## Usage Use the `blync` utility to directly control your Embrava BlyncLight: \b ```console $ blync -R # turn the light on with red color and leave it on $ blync --off # turn the light off $ blync -RG --dim # turn the light on with yellow color and dim $ blync -RBG # turn the light on with white color ``` Colors can be specified by values between 0 and 255 using the lower-case color options or using the upper-case full value options. \b ```console $ blync -r 127 # half intensity red $ blync -r 255 # full intensity red $ blync -R # also full intensity red $ blync -r 255 -b 255 -g 255 # full intensity white $ blync -RBG # full intensity white ``` If that's not enough fun, there are three builtin color modes: `fli`, `throbber`, and `rainbow`. All modes continue until the user terminates with a Control-C or platform equivalent. \b ```console $ blync fli $ blync throbber $ blync rainbow ``` ## Installation \b ```console $ python3 -m pip install blynclight $ python3 -m pip install git+https://github.com/JnyJny/blynclight.git # latest ``` This module depends on [hidapi](https://github.com/trezor/cython-hidapi), which supports Windows, Linux, FreeBSD and MacOS via a Cython module. """ if ctx.invoked_subcommand == "udev-rules": return try: light = BlyncLight.get_light(light_id, immediate=False) except BlyncLightNotFound as error: typer.secho(str(error), fg="red") raise typer.Exit(-1) from None assert not light.immediate light.red = red if not red_b else 255 light.blue = blue if not blue_b else 255 light.green = green if not green_b else 255 light.off = 1 if off else 0 light.dim = 1 if dim else 0 light.flash = 1 if flash > 0 else 0 light.speed = flash light.mute = 0 if play else 1 light.music = play light.play = 1 if play else 0 light.volume = volume light.repeat = 1 if repeat else 0 if not ctx.invoked_subcommand: if light.on and light.color == (0, 0, 0): light.color = DEFAULT_COLOR try: light.immediate = True for line in str(light).splitlines(): logger.info(line) except Exception as error: typer.secho(str(error), fg="red") raise typer.Exit(-1) from None raise typer.Exit() # Disable flashing for subcommands. light.flash = 0 ctx.obj = light
def callback( ctx: typer.Context, select_addons_dirs: Optional[List[Path]] = typer.Option( None, "--select-addons-dir", "-d", exists=True, file_okay=False, dir_okay=True, help=( "Select all installable addons found in this directory. " "This option may be repeated. " "The directories selected with this options are " "automatically added to the addons search path." ), show_default=False, ), select_include: Optional[str] = typer.Option( None, "--select-include", "--select", metavar="addon1,addon2,...", help=( "Comma separated list of addons to select. " "These addons will be searched in the addons path." ), ), select_exclude: Optional[str] = typer.Option( None, metavar="addon1,addon2,...", help=( "Comma separated list of addons to exclude from selection. " "This option is useful in combination with `--select-addons-dir`." ), ), select_core_addons: bool = typer.Option( False, "--select-core-addons", help="Select the Odoo core addons (CE and EE) for the given series.", show_default=False, ), addons_path: Optional[str] = typer.Option( None, help="Expand addons path with this comma separated list of directories.", ), addons_path_from_import_odoo: bool = typer.Option( True, help=( "Expand addons path by trying to `import odoo` and " "looking at `odoo.addons.__path__`. This option is useful when " "addons have been installed with pip." ), ), addons_path_python: str = typer.Option( "python", "--addons-path-python", show_default=False, metavar="PYTHON", help=( "The python executable to use when importing `odoo.addons.__path__`. " "Defaults to the `python` executable found in PATH." ), ), addons_path_from_odoo_cfg: Optional[Path] = typer.Option( None, exists=True, file_okay=True, dir_okay=False, readable=True, envvar="ODOO_RC", help=( "Expand addons path by looking into the provided Odoo configuration file. " ), ), separator: str = typer.Option( default=None, hidden=True, # deprecated ), odoo_series: Optional[OdooSeries] = typer.Option( None, envvar=["ODOO_VERSION", "ODOO_SERIES"], help="Odoo series to use, in case it is not autodetected from addons version.", ), verbose: int = typer.Option( 0, "--verbose", "-v", count=True, show_default=False, ), quiet: int = typer.Option( 0, "--quiet", "-q", count=True, show_default=False, ), version: Optional[bool] = typer.Option( None, "--version", callback=version_callback, is_eager=True, ), ) -> None: """Reason about Odoo addons manifests. The `--select-*` options of this command select addons on which the subcommands will act. The `--addons-path` options provide locations to search for addons. Run `manifestoo <subcommand> --help` for more options. """ echo.verbosity += verbose echo.verbosity -= quiet main_options = MainOptions() if separator: echo.warning( "--separator is deprecated as a global option. " "Please use the same option of list, list-depends." ) main_options.separator = separator # resolve addons_path if select_addons_dirs: main_options.addons_path.extend_from_addons_dirs(select_addons_dirs) if addons_path: main_options.addons_path.extend_from_addons_path(addons_path) if addons_path_from_import_odoo: main_options.addons_path.extend_from_import_odoo(addons_path_python) if addons_path_from_odoo_cfg: main_options.addons_path.extend_from_odoo_cfg(addons_path_from_odoo_cfg) echo.info(str(main_options.addons_path), bold_intro="Addons path: ") # populate addons_set main_options.addons_set.add_from_addons_dirs(main_options.addons_path) echo.info(str(main_options.addons_set), bold_intro="Addons set: ") # Odoo series if odoo_series: main_options.odoo_series = odoo_series else: detected_odoo_series = detect_from_addons_set(main_options.addons_set) if len(detected_odoo_series) == 0: echo.notice("No Odoo series detected in addons set") main_options.odoo_series = None elif len(detected_odoo_series) > 1: echo.notice( f"Different Odoo series detected in addons set: " f"{', '.join(detected_odoo_series)}" ) main_options.odoo_series = None else: main_options.odoo_series = detected_odoo_series.pop() # addons selection if select_addons_dirs: main_options.addons_selection.add_addons_dirs(select_addons_dirs) if select_include: main_options.addons_selection.add_addon_names(select_include) if select_exclude: main_options.addons_selection.remove_addon_names(select_exclude) if select_core_addons: ensure_odoo_series(main_options.odoo_series) assert main_options.odoo_series main_options.addons_selection.update(get_core_addons(main_options.odoo_series)) if main_options.addons_selection: echo.info(str(main_options.addons_selection), bold_intro="Addons selection: ") else: echo.notice("No addon selected, please use one of the --select options.") echo.info(f"{main_options.odoo_series}", bold_intro="Odoo series: ") # pass main options to commands ctx.obj = main_options
def main(ctx: typer.Context): ctx.obj = {"storage": LocalStorage()}
def _main_callback(ctx: Context): ctx.obj = dummy_context