def _subcommand_graph(sin, sout, serr, argv, efx): """Experimental graph-viz visualization of issues.. Use tags in your issue rows like `#after:[#123.4]` or `#part-of:[#123.4]` """ prog_name = (bash_argv := list(reversed(argv))).pop() foz = _formals_via(_formals_for_graph(), lambda: prog_name) vals, es = foz.terminal_parse(serr, bash_argv) if vals is None: return es if vals.get('help'): return foz.write_help_into(serr, _subcommand_graph.__doc__) readme = efx.resolve_issues_file_path() if readme is None: return 4 mon = efx.emission_monitor from pho._issues import issues_collection_via_ as func ic = func(readme, mon.listener) # .. kw = {} kw['targets'] = vals.get('add_target', ()) kw['show_group_nodes'] = vals.get('show_group_nodes', False) kw['show_identifiers'] = vals.get('show_identifiers', False) from pho._issues.graph import to_graph_lines_ as func for line in func(ic, mon.listener, **kw): sout.write(line) return mon.exitstatus
def build_hunks_iterator(): from text_lib.diff_and_patch import \ next_hunk_via_line_scanner as func, scanner_via_iterator scn = scanner_via_iterator(output_lines_of_git_log) if scn.empty: return yield func(scn) while scn.more: # expect a blank line because git puts one between the last hunk # line and the next nerfulous derfulous line. which is fine assert '\n' == scn.peek scn.advance() yield func(scn)
def _subcommand_use(sin, sout, serr, argv, efx): """Use a different readme (a shellable line. experimental) EXPERIMENTALLY you can try `$( pi use ./foo/bar/README.md )` You can get a list of available files with the `find` subcommand sibling to this one. """ prog_name = (bash_argv := list(reversed(argv))).pop() foz = _formals_via(_formals_for_use(), lambda: prog_name) vals, es = foz.terminal_parse(serr, bash_argv) if vals is None: return es if vals.get('help'): return foz.write_help_into(serr, _subcommand_use.__doc__) readme = vals.pop('readme') do_write = vals.pop('write', False) assert not vals mon = efx.emission_monitor from pho._issues.dotfile_ import write_dotfile as func here = efx.produce_dotfile_path() for line in func(here, readme, do_write, mon.listener): sout.write(line) if not line or '\n' != line[-1]: sout.write('\n') return mon.returncode
def _subcommand_close(sin, sout, serr, argv, efx): """Close an open issue.. Actually what this probably does is update *any* issue to become a '#hole'. It does not actually confirm that the issue is open, as far as we know. """ prog_name = (bash_argv := list(reversed(argv))).pop() foz = _formals_via(_formals_for_close(), lambda: prog_name) vals, es = foz.terminal_parse(serr, bash_argv) if vals is None: return es if vals.get('help'): return foz.write_help_into(serr, _subcommand_close.__doc__) eid = vals['identifier'] is_dry = vals.get('dry_run', False) mon = efx.emission_monitor listener = mon.listener readme = efx.resolve_issues_file_path() if readme is None: return 4 from pho._issues.edit import close_issue as func cs = func(readme, eid, listener, efx.produce_open_function()) if cs is None: return mon.exitstatus efx.apply_patch(cs.diff_lines, is_dry, listener) # result is t/f return mon.exitstatus
def line_offsets_and_lines(): el = retr.entity_section from . import start_line_offset_via_vendor_element_ as func start_LO = func(el) bot = retr.body_of_text all_file_lines = bot.lines from ._blocks_via_path import line_index_via_lines_ as func line_index = func(all_file_lines) stop_LO = line_index.stop_line_offset_of_vendor_element(el) # Advance the end pointer to include whitespace because why not last_LO = len(all_file_lines) while stop_LO < last_LO and '\n' == all_file_lines[stop_LO]: stop_LO += 1 return start_LO, stop_LO, bot
def _subcommand_open(sin, sout, serr, argv, efx): """Open an issue""" prog_name = (bash_argv := list(reversed(argv))).pop() foz = _formals_via(_formals_for_open(), lambda: prog_name) vals, es = foz.terminal_parse(serr, bash_argv) if vals is None: return es if vals.get('help'): return foz.write_help_into(serr, _subcommand_open.__doc__) # == BEGIN experiment def listener(chan, *rest): if 'verbose' != chan: return mon.listener(chan, *rest) if not be_verbose: return mon.listener('info', *rest) be_verbose = vals.pop('verbose', False) # == END dct = {'main_tag': '#open', 'content': vals['message']} is_dry = vals.get('dry_run', False) opn = efx.produce_open_function() mon = efx.emission_monitor readme = efx.resolve_issues_file_path() if readme is None: return 4 from pho._issues.edit import open_issue as func cs = func(readme, dct, listener, be_verbose=be_verbose, opn=opn) # cs = custom struct if cs is None: return mon.exitstatus dct = cs._asdict() if 'before_entity' in dct: before = dct.pop('before_entity') after = dct.pop('after_entity') else: before = None after = dct.pop('created_entity') if True: if before: serr.write(f"before: {before.to_line()}") serr.write(f"after: {after.to_line()}") else: serr.write(f"line: {after.to_line()}") efx.apply_patch(cs.diff_lines, is_dry, listener) # result is t/f return mon.exitstatus
def use_f(self): patch_file = self.big_patchfile.patches[n] from text_lib.diff_and_patch import \ file_patches_via_unified_diff_lines as func _2 = func(patch_file.diff_lines) _1 = patch_file class these_two: # #class-as-namespace patch_file = _1 file_patch, = tuple(_2) return these_two
def _counter_and_scanner_via_iterator(itr): from text_lib.magnetics.scanner_via import \ scanner_via_iterator as func, MUTATE_add_counter scn = func(itr) count = MUTATE_add_counter(scn) return count, scn
def _file_patches_via_patch_lines(lines): from text_lib.diff_and_patch import \ file_patches_via_unified_diff_lines as func return tuple(func(lines))
def build_file_patch(self): lines = self.end_state['diff_lines'] from text_lib.diff_and_patch import \ file_patches_via_unified_diff_lines as func file_patch, = tuple(func(lines)) return file_patch
def _pass_thru_context_manager(lines): from contextlib import nullcontext as func return func(lines)
def _error_monitor(serr): from script_lib.magnetics.error_monitor_via_stderr import func return func(serr, default_error_exitstatus=4)
def _formals_via(defs, prog_namer, sub_commands=None): from script_lib.cheap_arg_parse import formals_via_definitions as func return func(defs, prog_namer, sub_commands)
def pieces(rec): return (func(rec) for func in cel_renderers)
def apply_patch(diff_lines, is_dry, listener): from text_lib.diff_and_patch import apply_patch_via_lines as func return func(diff_lines, is_dry, listener, cwd=None) # t/f result
if readme_is_dash: # Read from stdin and each line is a readme path opened = sin else: # Open readme and each line is passed to the collection constructor opened = open(readme) elif readme_is_dash: # Pass stdin to open as the collection opened = _pass_thru_context_manager((sin,)) else: # Pass the readme path to the collection opened = _pass_thru_context_manager((readme,)) # Run the query from pho._issues import records_via_query_ as func itr = func(opened, user_query_tokens, sort_by, do_batch, mon.listener) two = next(itr) if two is None: return mon.returncode jsoner, counts = two # Prepare for output is_by_time = sort_by and 'by_time' == sort_by[0] is_complicated = do_batch or is_by_time # being "complicated" means needing strange columns if (fmt := vals.get('format')) is not None: allow = 'json', 'table' if fmt not in allow: _ = ''.join(('{', '|'.join(allow), '}')) inv = foz.invite_line