def main(): with Inform(notify_if_no_tty=True, version=version) as inform: try: # assure config and log directories exist to_path(CONFIG_DIR).mkdir(parents=True, exist_ok=True) to_path(DATA_DIR).mkdir(parents=True, exist_ok=True) inform.set_logfile(to_path(DATA_DIR, LOG_FILE)) # read command line cmdline = docopt(synopsis, options_first=True, version=version) command = cmdline["<command>"] args = cmdline["<args>"] if cmdline["--quiet"]: inform.quiet = True # find and run command settings = Settings(cmdline) cmd, cmd_name = Command.find(command) cmd.execute(cmd_name, args, settings, cmdline) except KeyboardInterrupt: display("Terminated by user.") except Error as e: e.terminate() except OSError as e: fatal(os_error(e)) done()
def main(): try: # read config file read_config() # read command line cmdline = docopt( __doc__.format(commands=Command.summarize()), version='avendesora {} ({})'.format(__version__, __released__), options_first=True, ) # start logging logfile = BufferedFile(get_setting('log_file'), True) Inform(logfile=logfile, hanging_indent=False, stream_policy='header', notify_if_no_tty=True) shlib.set_prefs(use_inform=True, log_cmd=True) # run the requested command Command.execute(cmdline['<command>'], cmdline['<args>']) done() except KeyboardInterrupt: output('\nTerminated by user.') terminate() except (PasswordError, Error) as e: e.terminate() except OSError as e: fatal(os_error(e)) done()
def get_argv(): argv = sys.argv[1:] if argv: # save the command line arguments for next time try: with open(saved_arguments_filename, 'w') as f: args = [a for a in argv if a not in ['-c', '--no-cache']] f.write('\n'.join(args)) except OSError as e: warn(os_error(e)) else: # command line arguments not give, reuse previous ones try: with open(saved_arguments_filename) as f: argv = f.read().split('\n') display('Using command:', ' '.join(argv)) except OSError: done() return argv
def read(self, name=None, path=None): """Recursively read configuration files. name (str): Name of desired configuration. Passed only when reading the top level settings file. Default is the default configuration as specified in the settings file, or if that is not specified then the first configuration given is used. path (str): Full path to settings file. Should not be given for the top level settings file (SETTINGS_FILE in CONFIG_DIR). """ if path: settings = PythonFile(path).run() parent = path.parent includes = Collection(settings.get(INCLUDE_SETTING)) else: # this is the generic settings file parent = self.config_dir if not parent.exists(): # config dir does not exist, create and populate it narrate('creating config directory:', str(parent)) parent.mkdir(mode=0o700, parents=True, exist_ok=True) for name, contents in [ (SETTINGS_FILE, INITIAL_SETTINGS_FILE_CONTENTS), ('root', INITIAL_ROOT_CONFIG_FILE_CONTENTS), ('home', INITIAL_HOME_CONFIG_FILE_CONTENTS), ]: path = parent / name path.write_text(contents) path.chmod(0o600) output( f'Configuration directory created: {parent!s}.', 'Includes example settings files. Edit them to suit your needs.', 'Search for and replace any fields delimited with << and >>.', 'Delete any configurations you do not need.', 'Generally you will use either home or root, but not both.', sep='\n') done() path = PythonFile(parent, SETTINGS_FILE) settings_filename = path.path settings = path.run() config = get_config(name, settings, self.composite_config_allowed) settings['config_name'] = config self.config_name = config includes = Collection(settings.get(INCLUDE_SETTING)) includes = [config] + list(includes.values()) if settings.get('passphrase'): if getmod(path) & 0o077: warn("file permissions are too loose.", culprit=path) codicil(f"Recommend running: chmod 600 {path!s}") self.settings.update(settings) for include in includes: path = to_path(parent, include) self.read(path=path) # default src_dirs if not given if not self.settings.get('src_dirs'): self.settings['src_dirs'] = [] # default archive if not given if 'archive' not in self.settings: if not 'prefix' in self.settings: self.settings[ 'prefix'] = '{host_name}-{user_name}-{config_name}-' self.settings['archive'] = self.settings['prefix'] + '{{now}}'
def read(self, name=None, path=None): """Recursively read configuration files. name (str): Name of desired configuration. Passed only when reading the top level settings file. Default is the default configuration as specified in the settings file, or if that is not specified then the first configuration given is used. path (str): Full path to settings file. Should not be given for the top level settings file (SETTINGS_FILE in CONFIG_DIR). """ if path: settings = PythonFile(path).run() parent = path.parent includes = Collection( settings.get(INCLUDE_SETTING), split_lines, comment="#", strip=True, cull=True, ) else: # this is the generic settings file parent = self.config_dir if not parent.exists(): # config dir does not exist, create and populate it narrate("creating config directory:", str(parent)) parent.mkdir(mode=0o700, parents=True, exist_ok=True) for name, contents in [ (SETTINGS_FILE, INITIAL_SETTINGS_FILE_CONTENTS), ("root", INITIAL_ROOT_CONFIG_FILE_CONTENTS), ("home", INITIAL_HOME_CONFIG_FILE_CONTENTS), ]: path = parent / name path.write_text(contents) path.chmod(0o600) output( f"Configuration directory created: {parent!s}.", "Includes example settings files. Edit them to suit your needs.", "Search for and replace any fields delimited with << and >>.", "Delete any configurations you do not need.", "Generally you will use either home or root, but not both.", sep="\n", ) done() path = PythonFile(parent, SETTINGS_FILE) self.settings_filename = path.path settings = path.run() config = get_config(name, settings, self.composite_config_response, self.show_config_name) settings["config_name"] = config self.config_name = config includes = Collection(settings.get(INCLUDE_SETTING)) includes = [config] + list(includes.values()) if settings.get("passphrase"): if getmod(path) & 0o077: warn("file permissions are too loose.", culprit=path) codicil(f"Recommend running: chmod 600 {path!s}") self.settings.update(settings) # read include files, if any are specified for include in includes: path = to_path(parent, include) self.read(path=path)
def plot_signals(): try: # process command line {{{2 cmdline = docopt(__doc__, argv=get_argv()) psf_file = get_psf_filename(cmdline['--psf-file']) args = cmdline['<signal>'] title = cmdline['--title'] svg_file = cmdline['--svg'] dB = cmdline['--db'] mag = cmdline['--mag'] phase = cmdline['--ph'] use_cache = not cmdline['--no-cache'] linestyle = '' if cmdline['--just-points'] else '-' marker = '.' if cmdline['--mark-points'] or cmdline['--just-points'] else '' # Open PSF file {{{2 psf = PSF(psf_file, sep=':', use_cache=use_cache) sweep = psf.get_sweep() to_plot = expand_args(psf.signals.keys(), args) # x_name = sweep.name if not sweep: with Quantity.prefs(map_sf = Quantity.map_sf_to_greek, prec = print_prec): to_print = [] width = 0 for arg in to_plot: pair = arg.split('-') if len(pair) == 2: psig = psf.get_signal(pair[0]) nsig = psf.get_signal(pair[1]) if psig.units != nsig.units: warn( f'incompatible units ({psig.units} != {nsig.units}', culprit=arg ) units = psig.units access = psig.access y_data = Quantity(psig.ordinate - nsig.ordinate, units) name = f'{access}({psig.name},{nsig.name})' else: sig = psf.get_signal(arg) access = sig.access name = f'{access}({sig.name})' y_data = sig.ordinate to_print.append((name, y_data)) width = max(width, len(name)) for name, y_data in to_print: display(f'{name:>{width+4}} = {y_data}') return x_units = sweep.units x_data = sweep.abscissa x_formatter = FuncFormatter( lambda v, p: Quantity(v, x_units).render() ) # Process arguments {{{2 waves = [] y_units = set() for arg in to_plot: pair = arg.split('-') if len(pair) == 2: psig = psf.get_signal(pair[0]) nsig = psf.get_signal(pair[1]) name = arg if psig.units != nsig.units: warn( f'incompatible units ({psig.units} != {nsig.units}', culprit=arg ) units = psig.units y_data = psig.ordinate - nsig.ordinate else: sig = psf.get_signal(arg) name = arg units = sig.units y_data = sig.ordinate if units == 'Unitless': units = '' if dB: y_data = 20*np.log10(np.absolute(y_data)) units = 'dB' + units elif mag: y_data = np.absolute(y_data) elif phase: y_data = np.angle(y_data, deg=True) units = '°' elif np.iscomplexobj(y_data): y_data = np.absolute(y_data) waves.append((name, y_data, units)) y_units.add(units) if not y_units: raise Error(f'{plural(args):no match/es}.', culprit=args) y_formatters = { u: FuncFormatter( lambda v, p, u=u: str(Quantity(v, psf.units_to_unicode(u))) ) for u in y_units } xy_formatters = {} x_kind = signal_kinds.get(x_units, x_units) for u in y_units: y_kind = signal_kinds.get(x_units, u) xy_label = f"{x_kind} = {{x}}, {y_kind} = {{y}}" units = psf.units_to_unicode(u) xy_formatters[u] = lambda x, y: xy_label.format( x = Quantity(x, x_units).render(prec=cursor_prec), y = Quantity(y, units).render(prec=cursor_prec) ) # Generate the plot {{{2 if svg_file: matplotlib.use('SVG') figure, axes = plt.subplots(len(y_units), 1, sharex=True, squeeze=False) for i, units in enumerate(y_units): for sig_name, y_data, sig_units in waves: if sig_units == units: axes[i, 0].plot( x_data, y_data, label = sig_name, marker = marker, linestyle = linestyle, linewidth = 2, ) axes[i, 0].legend(frameon=False, loc='best') axes[i, 0].set_xscale('log' if psf.log_x(sweep) else 'linear') axes[i, 0].set_yscale('log' if psf.log_y(sweep) and not dB else 'linear') axes[i, 0].xaxis.set_major_formatter(x_formatter) axes[i, 0].yaxis.set_major_formatter(y_formatters[units]) axes[i, 0].format_coord = xy_formatters[units] if title: plt.suptitle(title) if svg_file: plt.savefig(svg_file) else: plt.show() except Error as e: e.terminate() except KeyboardInterrupt: done()
def main(): try: # Read command line {{{1 cmdline = docopt(__doc__) keys = cmdline["--keys"].split(",") if cmdline["--keys"] else [] update = cmdline["--update"].split(",") if cmdline["--update"] else [] skip = cmdline["--skip"].split(",") if cmdline["--skip"] else [] Inform( narrate=cmdline["--narrate"] or cmdline["--verbose"], verbose=cmdline["--verbose"], logfile=".sshdeploy.log", prog_name=False, flush=True, version=__version__, ) if keys and not cmdline["--trial-run"]: fatal( "Using the --keys option results in incomplete authorized_keys files.", "It may only be used for testing purposes.", "As such, --trial-run must also be specified when using --keys.", sep="\n", ) # Generated detailed help {{{1 if cmdline["manual"]: from pkg_resources import resource_string try: Run(cmd=["less"], modes="soeW0", stdin=resource_string("src", "manual.rst").decode("utf8")) except OSError as err: error(os_error(err)) terminate() # Read config file {{{1 try: config_file = cmdline.get("--config-file") config_file = config_file if config_file else "sshdeploy.conf" contents = to_path(config_file).read_text() except OSError as err: fatal(os_error(err)) code = compile(contents, config_file, "exec") config = {} try: exec(code, config) except Exception as err: fatal(err) # Move into keydir {{{1 keydir = cmdline["--keydir"] keydir = to_path(keydir if keydir else "keys-" + date) if cmdline["generate"]: comment("creating key directory:", keydir) rm(keydir) mkdir(keydir) cd(keydir) elif cmdline["distribute"]: cd(keydir) # determine default values for key options defaults = {} for name, default in [ ("keygen-options", DefaultKeygenOpts), ("abraxas-account", DefaultAbraxasAccount), ("remote-include-filename", DefaultRemoteIncludeFilename), ]: defaults[name] = config.get(name, default) # Generate keys {{{1 if cmdline["generate"]: for keyname in sorted(config["keys"].keys()): data = config["keys"][keyname] if keys and keyname not in keys: # user did not request this key continue # get default values for missing key options for option in defaults: data[option] = data.get(option, defaults[option]) # generate the key key = Key(keyname, data, update, skip, cmdline["--trial-run"]) key.generate() # Publish keys {{{1 elif cmdline["distribute"]: for keyname in sorted(config["keys"].keys()): data = config["keys"][keyname] if keys and keyname not in keys: continue # user did not request this key # get default values for missing key options for option in defaults: data[option] = data.get(option, defaults[option]) # publish the key pair to clients key = Key(keyname, data, update, skip, cmdline["--trial-run"]) key.publish_private_key() key.gather_public_keys() # publish authorized_keys files to servers {{{1 if cmdline["distribute"]: for each in sorted(AuthKeys.known): authkey = AuthKeys.known[each] authkey.publish() authkey.verify() # Process hosts {{{1 elif cmdline["test"] or cmdline["clean"] or cmdline["hosts"]: hosts = set() for keyname, data in config["keys"].items(): if keys and keyname not in keys: continue # user did not request this key # add servers to list of hosts for server, options in data["servers"].items(): if update and server not in update or server in skip: continue if "bypass" not in options: hosts.add(server) # add clients to list of hosts for client in data["clients"].keys(): if update and client not in update or client in skip: continue hosts.add(client) # process the hosts if cmdline["test"]: # test host for host in sorted(hosts): test_access(host) elif cmdline["clean"]: # clean host for host in sorted(hosts): clean(host) else: # list hosts for host in sorted(hosts): display(host) except OSError as err: error(os_error(err)) except KeyboardInterrupt: display("Killed by user") done()
def read_config(self, name=None, path=None, queue=None): """Recursively read configuration files. name (str): Name of desired configuration. Passed only when reading the top level settings file. Default is the default configuration as specified in the settings file, or if that is not specified then the first configuration given is used. path (str): Full path to settings file. Should not be given for the top level settings file (SETTINGS_FILE in CONFIG_DIR). """ if path: # we are reading an include file settings = PythonFile(path).run() parent = path.parent includes = Collection( settings.get(INCLUDE_SETTING), split_lines, comment="#", strip=True, cull=True, ) else: # this is the base-level settings file parent = self.config_dir if not parent.exists(): # config dir does not exist, create and populate it narrate("creating config directory:", str(parent)) parent.mkdir(mode=0o700, parents=True, exist_ok=True) for fname, contents in [ (SETTINGS_FILE, INITIAL_SETTINGS_FILE_CONTENTS), ("root", INITIAL_ROOT_CONFIG_FILE_CONTENTS), ("home", INITIAL_HOME_CONFIG_FILE_CONTENTS), ]: path = parent / fname path.write_text(contents) path.chmod(0o600) output( f"Configuration directory created: {parent!s}.", "Includes example settings files. Edit them to suit your needs.", "Search for and replace any fields delimited with ⟪ and ⟫.", "Delete any configurations you do not need.", "Generally you will use either home or root, but not both.", sep="\n", ) done() # read the shared settings file path = PythonFile(parent, SETTINGS_FILE) self.settings_filename = path.path settings = path.run() # initialize the config queue if not queue: # this is a request through the API queue = ConfigQueue() if queue.uninitialized: queue.initialize(name, settings) config = queue.get_active_config() self.configs = queue.configs self.log_command = queue.log_command # save config name settings["config_name"] = config self.config_name = config if not config: # this happens on composite configs for commands that do not # need access to a specific config, such as help and configs self.settings.update(settings) return # get includes includes = Collection(settings.get(INCLUDE_SETTING)) includes = [config] + list(includes.values()) if settings.get("passphrase"): if getmod(path) & 0o077: warn("file permissions are too loose.", culprit=path) codicil(f"Recommend running: chmod 600 {path!s}") self.settings.update(settings) # read include files, if any are specified for include in includes: path = to_path(parent, include) self.read_config(path=path)
def test_pardon(): with messenger() as (msg, stdout, stderr, logfile): try: terminate() assert False except SystemExit as e: assert e.args == (0,) try: raise Error('hey now!', culprit='nutz', extra='foo', codicil='putz') assert False except Error as err: assert err.get_message() == 'hey now!' assert err.get_culprit() == ('nutz',) assert err.get_codicil() == ('putz',) assert join_culprit(err.get_culprit()) == 'nutz' assert err.extra == 'foo' assert str(err) == 'nutz: hey now!' assert errors_accrued() == 0 # errors don't accrue until reported try: raise Error( 'hey now!', culprit=('nutz', 'crunch'), extra='foo', codicil=('putz', 'toodle'), ) assert False except Error as err: assert err.get_message() == 'hey now!' assert err.get_culprit() == ('nutz', 'crunch') assert err.get_codicil() == ('putz', 'toodle') assert join_culprit(err.get_culprit()) == 'nutz, crunch' assert err.extra == 'foo' assert str(err) == 'nutz, crunch: hey now!' assert err.get_message() == 'hey now!' assert err.get_message('{extra}, {}') == 'foo, hey now!' assert err.render() == 'nutz, crunch: hey now!' assert err.render('{extra}, {}') == 'nutz, crunch: foo, hey now!' assert errors_accrued() == 0 # errors don't accrue until reported try: err.terminate() assert False except SystemExit as e: assert e.args == (1,) try: done() assert False except SystemExit as e: assert e.args == (0,) try: rv = done(exit=False) assert rv == 0 except SystemExit as e: assert False try: terminate() assert False except SystemExit as e: assert e.args == (1,) try: rv = terminate(exit=False) assert rv == 1 except SystemExit as e: assert False try: rv = terminate(True, exit=False) assert rv == 1 except SystemExit as e: assert False try: rv = terminate('fuxit', exit=False) assert rv == 1 except SystemExit as e: assert False try: rv = terminate(6, exit=False) assert rv == 6 except SystemExit as e: assert False try: terminate_if_errors() assert False except SystemExit as e: assert e.args == (1,) assert True try: rv = terminate_if_errors(exit=False) assert rv == 1 except SystemExit as e: assert False try: raise Error('hey now', culprit=('nutz', 347)) assert False except Error as err: assert err.get_message() == 'hey now' assert err.get_culprit() == ('nutz', 347) assert join_culprit(err.get_culprit()) == 'nutz, 347' assert join_culprit(err.get_culprit(66)) == '66, nutz, 347' assert join_culprit(err.get_culprit(('a', 'b'))) == 'a, b, nutz, 347' assert str(err) == 'nutz, 347: hey now'