Beispiel #1
0
 def stream_join(itr):  # :[#612.4] near [#611] things we do with scanners
     scn = func(itr)
     yield scn.next()  # ..
     while scn.more:
         yield sep
         yield scn.next()
Beispiel #2
0
def _schema_and_entities_via_lines(lines, listener):  # #testpoint:KS
    def main():
        make_sure_there_is_at_least_one_line()
        make_sure_the_line_has_at_least_one_cell_and_note_num_fields()
        make_sure_the_cells_all_translate_to_valid_field_names()
        return _MinimalSchema(self.field_name_keys), entities()

    def entities():
        rang, num, ks = self.range, self.num_fields, self.field_name_keys
        while scn.more:
            line = scn.next()
            line_ast = line_AST_via_line(line)
            cells = line_ast.cells
            if num != len(cells):
                d = counter()
                _whine_about_num_fields_variation(listener, line_ast, d, num)
                break  # provision #here3
            k_v_s = ((ks[i], cells[i].cell_content) for i in rang)

            # [#873.5] no None, no empty string
            yield _MinimalEntity({k: v for k, v in k_v_s if len(v)})

    def make_sure_the_cells_all_translate_to_valid_field_names():
        def is_bad(field_name):
            return False if field_name_rx.match(field_name) else True

        cells = (line_ast := self.first_line_AST).cells
        titles = tuple(cell.cell_content for cell in cells)
        self.range = range(0, self.num_fields)
        bads = tuple(i for i in self.range if is_bad(titles[i]))
        if len(bads):
            _whine_about_bad_field_names(listener, bads, line_ast)
            raise stop()
        scn.advance()

        untitleize = _build_untitleizer()
        self.field_name_keys = tuple(untitleize(title) for title in titles)

    field_name_rx = re.compile(
        r'''^
        [a-zA-Z]  [a-zA-Z-0-9]*  (?: [ ][a-zA-Z0-9]+ ) *
    \Z''', re.VERBOSE)

    def make_sure_the_line_has_at_least_one_cell_and_note_num_fields():
        first_line = scn.peek
        leng = len((ast := line_AST_via_line(first_line)).cells)
        if 0 == leng:
            xx("blank for first line?")
        self.num_fields = leng
        self.first_line_AST = ast

    def make_sure_there_is_at_least_one_line():
        if scn.empty:
            xx("empty line stream")

    def line_AST_via_line(line):
        spans = tuple(spans_via_line(line))
        cells = tuple(cell_sexps_via_spans(spans, line))  # #wish [#613.1]
        last_end = spans[-1][1] if len(spans) else 0
        return ast_via_sexp(('line', cells, line[last_end:]))

    def cell_sexps_via_spans(spans, line):
        prev = 0
        for beg, end in spans:
            head = line[prev:beg]
            tail = line[beg:end]
            yield 'cell', head, tail
            prev = end

    # == Functions that get built

    def throwing_listener(sev, *rest):
        listener(sev, *rest)
        if 'error' == sev:
            raise stop()

    from text_lib.magnetics.ast_via_sexp_via_definition import \
        lazy_classes_collection_via_AST_definitions as func
    ast_via_sexp = func(_AST_definitions()).AST_via_sexp

    spans_via_line = _spans_of_old(throwing_listener)

    # == Other local globals

    scnlib = _scnlib()
    scn = scnlib.scanner_via_iterator(lines)
    counter = scnlib.MUTATE_add_counter(scn)

    # ==

    class self:  # #class-as-namespace
        pass

    class stop(RuntimeError):
        pass

    try:  # #here3
        return main()
    except stop:
        pass
def _build_lazy_AST_classes():
    from text_lib.magnetics.ast_via_sexp_via_definition import \
        lazy_classes_collection_via_AST_definitions as func
    return func(_sexp_definitions())
def _scanner_via_iterator(itr):
    from text_lib.magnetics.scanner_via import scanner_via_iterator as func
    return func(itr)
def updated_file_blocks_via_(plan, listener):
    before_ks = set((*locals().keys(), 'before_ks'))

    def merge_head_blocks(cb, tb):
        return same_merge(cb, tb)  # hi.

    def merge_tail_blocks(cb, tb):
        return same_merge(cb, tb)  # hi.

    def update_client_case_with_template_case(ci, ti):
        ccase = client_maybe_cases[ci]
        tcase = template_cases[ci]
        nu_case = updated_case_via(ccase, tcase, throwing_listener)
        use(nu_case)

    def insert_case(offset):
        use_me = surface_case_via(template_cases[offset])
        use(use_me)

    def insert_plain(tb):
        use(tb.without_directive())

    locs = locals()
    actions = {k: locs[k] for k in (set(locs.keys()) - before_ks)}

    def same_merge(cb, tb):
        # At writing, this is our experimental behavior for both the header
        # and the footer, when we have a merge:

        # 1) If the template block has marked itself as default, stand down
        if tb.has_directive:
            assert 'default' == tb._directive_body
            return use(cb)

        # 2) If the code looks the same ignoring comments & w.s, stand down
        if _blocks_are_same_ignoring_comments_and_whitespace(cb, tb):
            return use(cb)

        # 3), otherwise (YIKES) overwrite whatever is there
        use(tb.without_directive())

    client_maybe_cases = plan.client_maybe_cases
    template_cases = plan.template_cases

    def use(block):
        result_blocks.append(block)  # hi.

    result_blocks = []

    def throwing_listener(sev, *rest):
        listener(sev, *rest)
        if 'error' == sev:
            raise stop()

    class stop(RuntimeError):
        pass

    from ._updated_case_via_two_cases import \
        updated_case_via_ as updated_case_via, \
        build_case_surfacer_function_ as func

    surface_case_via = func(throwing_listener)

    try:
        for (action_k, *args) in plan.steps:
            actions[action_k](*args)
        return tuple(result_blocks)
    except stop:
        pass