import argparse

__author__ = 'moskupols'


def evolution(title=None, resource=None, *, modifiers=None, explanations=None):
    from copy import copy
    from hb_res.explanations import Explanation
    from preparation.resources.Resource import resource_by_name

    def evolute(start, mods):
        cur = copy(start)
        yield (None, cur)
        for m in mods:
            cur = m(cur)
            yield (m, cur)
            if cur is None:
                break

    if isinstance(resource, str):
        resource = resource_by_name(resource)()

    assert modifiers is not None or hasattr(resource, 'modifiers')
    if modifiers is None:
        modifiers = resource.modifiers

    assert (title is not None and resource is not None) ^ (explanations is not None)
    if explanations is None:
        explanations = filter(lambda e: e.title == title, resource)

    if isinstance(explanations, Explanation):
        return tuple(evolute(explanations, modifiers))
    else:
        for start_expl in explanations:
            yield (start_expl, tuple(evolute(start_expl, modifiers)))


def make_argparser():
    from preparation.resources.Resource import names_registered

    parser = argparse.ArgumentParser(description='View how some explanation(s) evolute ')

    formats = {
        'oneline': '{mod!s:>50} -> ("{result.title}" := "{result.text}")',
        'wide': '{mod!s:>60} -> {result!r}',
        'twoline': 'after {mod}:\n --> {result!r}'
    }

    fmt_args = parser.add_mutually_exclusive_group()
    fmt_args.add_argument('--format',
                          default=formats['twoline'],
                          help='''Self argument for str.format call while printing evolution.
                          If omitted, same as --twoline.
                          ''')
    for name, fmt in formats.items():
        fmt_args.add_argument('--' + name,
                              dest='format',
                              action='store_const',
                              const=fmt,
                              help='Format as ' + repr(fmt))

    parser.add_argument('resource',
                        choices=names_registered(),
                        help='resource to use')
    parser.add_argument('title',
                        nargs='+',
                        help='''title (titles) to show.
                        They will be used to find raw explanations yielded by the resource's parser,
                        so should be exactly in the same format the parser gives it.''')
    return parser


def main(args=None):
    if not isinstance(args, argparse.Namespace):
        parser = make_argparser()
        args = parser.parse_args(args)
    first_story = True
    for title in args.title:
        stories = evolution(title=title, resource=args.resource)
        for start_expl, story in stories:
            if first_story:
                first_story = False
            else:
                print()
            for mod, result in story:
                print(args.format.format(mod=mod, result=result))


if __name__ == '__main__':
    main()

@@ -90,16 +90,23 @@
 odifiers
+: tuple
 =None, e
@@ -120,24 +120,1034 @@
 ions=None):%0A
+    %22%22%22%0A    Generates evolution stories of given explanations or explanations with known title%0A    being affected by modifiers of given resource or just given modifiers.%0A%0A    Each story is a tuple of pairs (modifier, result). The first element of this tuple is always%0A     (None, initial explanation).%0A%0A    For call you should specify modifiers and explanations, but you can do it implicitly%0A    by specifying resource and title to seek in the resource's output.%0A%0A    If explanations is specified and is instance of Explanation, the function returns evolution story.%0A    Otherwise it returns sequence (start_explanation, evolution story).%0A%0A    :param title: title of explanation to seek in exactly the same format as generated by the resource.%0A    :param resource: name or resource itself.%0A    :param modifiers: if not specified, modifiers are taken from resource.modifiers.%0A    :param explanations: if not specified, explanations are taken from (e for e in resource if e.title == title).%0A    :return:%0A    %22%22%22%0A
     from cop
@@ -1838,24 +1838,31 @@
 s = 
-filter(lambda e:
+(e for e in resource if
  e.t
@@ -1874,26 +1874,16 @@
 == title
-, resource
 )%0A%0A    i

Add evolution docstring