Пример #1
0
def ask(query):
    """
    Got a question? Google it!
    Looks on google for a hit from stack overflow matching a search query
    and prints it out nicely formatted
    """
    if not isinstance(query, str):
        raise ValueError(
            f"Search query must be a string, not {_class_name(query)}")
    answer = get_google(query,
                        n_answers=10,
                        best_so=True,
                        mute=True,
                        break_on_best=True)

    if answer is not None:
        _parse_so_top_answer(answer)

        console.print(
            f"\nTo continue reading about this question, head to: [{lightblue}]{answer}\n"
        )
    else:
        warn(
            "Failed to get a SO",
            f"Looked on google for {query}, but didn't find a Stack Overflow answer, sorry.",
        )
Пример #2
0
def get_stackoverflow(query):
    """
    Prints the results of interrogating SO
    with a search query (str).
    """
    # get link to top question
    questionlink, search_url = _get_link_so_top_answer(query)

    if questionlink is None:
        warn(
            "Failed to find anything on stack overflow, sorry.",
            "While parsing the URL with the top SO answer, could not detect any answer. Nothing to report",
        )
        return

    _parse_so_top_answer(questionlink)

    out = Text.from_markup(f"""
[{white}]Link to top [{lightsalmon}]Stack Overflow[/{lightsalmon}] question for the error:
        [{ls}]{questionlink}[/{ls}]

[{white}]To find more related answers on [{lightsalmon}]Stack Overflow[/{lightsalmon}], visit:
        [{ls}]{search_url}[/{ls}]
""")

    console.print(out)
Пример #3
0
    def show(self, n_rows=15):
        """ Renders a rich table with dict contents """
        tb = Table(
            box=box.SIMPLE,
            title="Dict content",
            title_style="magenta",
            show_lines=True,
            show_edge=True,
            show_footer=True,
            footer_style="dim",
        )
        tb.add_column(
            header="key",
            header_style="yellow",
            justify="center",
            footer=f"{len(self)} entries",
            width=20,
        )
        tb.add_column(header="value",
                      header_style="yellow",
                      justify="center",
                      width=30)

        for n, (k, v) in enumerate(self.items):
            tb.add_row(f"[{orange}]{k}", f"[{mocassin}]{v}")

            if n == n_rows:
                tb.add_row("truncated at", f"{n_rows} rows...")
                break

        console.print(tb)
Пример #4
0
def print_methods_table(found, class_obj, name):
    """
    Prints a table with the methods found by search_class_method

    :param found: dictionary with found functions
    :param class_obj: class obj. Where the methods where searched in
    :param name: str, None. Query string
    """

    # make rich table
    table = Table(
        show_header=True,
        header_style="bold magenta",
        box=box.SIMPLE,
    )
    table.add_column("#", style="dim", width=3, justify="center")
    table.add_column("name", style="bold " + lightgreen)
    table.add_column("Class", justify="left")
    table.add_column("", style="bold " + lightgreen)

    table.add_column("Module", justify="left")
    table.add_column("Signature")

    # list methods
    count = 0
    for obj, methods in found.items():
        for k, v in methods.items():
            if not isfunction(v):
                continue  # skip docstrings etc

            lineno = inspect.getsourcelines(getattr(class_obj, k))[1]
            cs = get_class_that_defined_method(v)
            sig = textify(str(signature(cs)), maxlen=50)

            if cs == class_obj:
                cs = f"[{lightgreen}]{_name(cs)}[/{lightgreen}]"
                method_name = k
            else:
                cs = f"[{salmon}]{_name(cs)}[/{salmon}]"
                method_name = f"[{salmon}]{k}[/{salmon}]"

            module = f"[white]{_module(v)} [dim](line: {lineno})"

            table.add_row(
                str(count),
                method_name,
                cs,
                "",
                module,
                sig,
            )
            count += 1

    st = f"bold black on {mocassin}"
    console.print(
        f"\n[{mocassin}]Looking for methods of [{st}] {_name(class_obj)} ({_module(class_obj)}) [/{st}] with query name: [{st}] {name} [/{st}]:",
        table,
    )
Пример #5
0
 def inner(*args, **kwargs):
     if not connected_to_internet():
         console.print(
             "No internet connection found.",
             f"Can't proceed with the function: {_name(func)}.",
             sep="\n",
         )
     else:
         return func(*args, **kwargs)
Пример #6
0
def search_class_method(class_obj,
                        name="",
                        print_table=True,
                        include_parents=True,
                        **kwargs):
    """
    Given a python class, it finds allclass methods whose name includes
    the given search string (name)

    :param class_obj: a python Class. Should not be a class instance, but a point to the class object
    :param name: str, optional. Returns only methods which have this string in the name. If not is given returns all methods
    :param print_table: bool, optional. If True it prints a table with all the found methods
    :param bool: if true it looks for methods in parents of the class_obj as well

    :returns: dict with all the methods found
    """
    if not isclass(class_obj):
        raise ValueError(
            "find_class_method expects a python Class object as argument")

    # Look for methods in the class and in the parents
    objs = [class_obj]

    if include_parents:

        def get_parent_classes(obj):
            if obj is None:
                return

            parents = obj.__base__
            if parents is None:
                return

            for p in [parents]:
                get_parent_classes(p)
            objs.append(parents)

        get_parent_classes(class_obj)

    found = {}
    for obj in objs:
        found[obj] = {k: v for k, v in obj.__dict__.items() if name in k}
    found = {k: v for k, v in found.items() if v.keys()}

    if not found.keys():
        console.print(
            f"[magenta]No methods found in class {class_obj} with query: {name}"
        )
        return None

    if print_table:
        print_methods_table(found, class_obj, name)
Пример #7
0
def search_module_function(module,
                           name="",
                           print_table=True,
                           include_class=True,
                           **kwargs):
    """
    Given a module (e.g. matplotlib.pyplot) finds all the functions
    in it whose name includes the given search string.

    :param module: python module (e.g. numpy)
    :param name: str, optional. Search string, if none is passed it returns all functions
    :param print_table: bool, optional.  If True it prints a table with all the found functions

    :returns: dict with all the functions found
    """
    def same_module(obj, module):
        return inspect.getmodule(obj) == module

    # Get all the submodules
    modules = get_submodules(module)

    # grab all function names that contain `name` from the module
    found = {}
    for modname, mod in modules.items():
        # get only functions
        funcs = [
            o for o in inspect.getmembers(mod)
            if isfunction(o[1]) or isclass(o[1])
        ]

        if not include_class:
            funcs = [o for o in funcs if not isclass(o[1])]

        # keep only functions from this module
        mod_funcs = [o for o in funcs if same_module(o[1], mod)]

        # keep only functions matching the query name
        filtered = [o[0] for o in mod_funcs if name.lower() in o[0].lower()]

        found[(modname, mod)] = filtered
    found = {k: v for k, v in found.items() if v}

    if not len(found.keys()):
        console.print(
            f"[magenta]No functions found in module {module} with query: {name}"
        )
        return None

    # Print a table with the results
    if print_table:
        print_funcs_table(found, module, name)
Пример #8
0
def _what_locals(**kwargs):
    """
    Prints all variables, classes and modules in the local scope where `what` was called
    """
    locs, local_stack = _get_local_stacks()
    types = {
        k: _get_type_info(l, all_locals=True)[2]
        for k, l in local_stack.items()
    }

    # divide based on object type
    classes = {k: locs[k] for k, t in types.items() if "type" in t}
    variables = {
        k: locs[k]
        for k, t in types.items()
        if "type" not in t and "module" not in t
    }
    modules = {k: locs[k] for k, t in types.items() if "module" in t}

    # create a table to organize the results
    table = Table(show_edge=False, show_lines=False, expand=False, box=None)
    table.add_column()

    # render each group of objects and add to table
    for group, name in zip(
        [variables, classes, modules], ["Variables", "Classes", "Modules"]
    ):
        cleaned_group = {
            k: v for k, v in group.items() if not k.startswith("__")
        }

        if not len(cleaned_group.keys()):
            continue  # nothing to show

        # Get the correct color for the obj type
        obj = list(cleaned_group.items())[0][1].obj
        _type = _get_type_info(obj, all_locals=True)[2]
        type_color = _get_type_color(_type)

        # add to table
        table.add_row(
            f"[bold][{type_color}]{name}[/{type_color}][{mocassin}] in local frame."
        )
        table.add_row(render_scope(cleaned_group, just_table=True))

    # print!
    console.print(
        Panel.fit(table, width=PANEL_WIDTH + 10, border_style=lightblue)
    )
Пример #9
0
def print_funcs_table(found, module, name):
    """
    Prints a table with the functions found by search_module_function

    :param found: dictionary with found functions
    :param module: module obj. Where the functions where searched in
    :param name: str, None. Query string
    """
    table = Table(
        show_header=True,
        header_style="bold magenta",
        box=box.SIMPLE,
    )
    table.add_column("#", style="dim", width=3, justify="center")
    table.add_column("name", style="bold " + lightgreen)
    table.add_column("Module")
    table.add_column("Arguments", style=lightgray)

    count = 0
    for (modname, mod), funcs in found.items():
        for f in funcs:
            # Get clean docstring
            func = getattr(mod, f)
            doc = clean_doc(inspect.getdoc(func), maxn=101)

            if not doc or doc is None:
                doc = "no docstring"

            # Get clickable link to module file
            if not isclass(func):
                lineno = getsourcelines(func)[-1]
                text = f"{modname} [dim](line: {lineno})"
            else:
                f = f"[{yellow}]{f}[/{yellow}]"
                text = f"[{yellow}]{modname}[/{yellow}]"

            # add to table
            try:
                table.add_row(str(count), f, text, str(signature(func)))
            except ValueError:  # signature has problems with some builtins
                table.add_row(str(count), f, text, "")
            count += 1

    st = f"black bold on {mocassin}"
    console.print(
        f"[{mocassin}]Looking for functions of [{st}] {_name(module)} [/{st}] with query name [{st}] {name if name else 'no-name'} [/{st}]:",
        table,
    )
Пример #10
0
def _parse_so_top_answer(url):
    """
    Parses a link to a SO question
    to return the formatted text of the question and top answer
    """
    # get content
    res = requests.get(url)
    if not res.ok:
        return None

    # get question and answer
    console.print("[white]Parsing SO answer...")
    bs = BeautifulSoup(res.content, features="html.parser")

    question = bs.find("div", attrs={"class": "question"})
    answer = bs.find("div", attrs={"class": "answer"})

    if answer is None or question is None:
        warn(
            "Failed to parse SO answer",
            f"We tried to parse the SO question but failed...",
        )
        return

    # Print as nicely formatted panels
    panels = []
    for name, obj, color in zip(["Question", "Answer"], [question, answer],
                                [lightsalmon, lightblue]):
        panels.append(_style_so_element(obj, name, color))

    if panels:
        console.print(
            f"[{mocassin}]\n\nAnswer to the top [i]Stack Overflow[/i] answer for your question.",
            Columns(
                panels,
                equal=True,
                width=88,
            ),
            sep="\n",
        )
    else:
        warn(
            "Failed to find answer on the SO page",
            "While parsing the URL with the top SO answer, could not detect any answer. Nothing to report",
        )
Пример #11
0
def get_answers(hide_panel=False):
    """
    Looks for solution to the last error encountered (as cached).
    Prints the error message, it's meaning and links
    to possible answers on google and stack overflow.
    It also parses the question and first answer from the top hit from
    google if that's a link to a SO page.

    :param hide_panel: bool, False. If true the panel with
        the error message is hidden
    """
    try:
        query, msg = load_cached()
    except ValueError:
        warn(
            "Failed to load cached error.",
            "This could be because no error had been cached, or it could be a bug",
        )

    # show a panel with a recap of the error message
    if not hide_panel:
        out = f"""
    [bold {white}]Searching online for solutions to:

            [bold {white}]>[/bold {white}] [bold {salmon}]{query}",
            [bold {white}]>[/bold {white}]
            [bold {white}]>[/bold {white}]    [{salmon}]{query.split(":")[0]}:[/{salmon}][{lightgray}] {msg}',
            """

        panel = Panel.fit(
            Text.from_markup(out),
            padding=(1, 2),
            border_style=salmon,
        )
        console.print(panel)
    else:
        console.print("\n")

    # ask google and stack overflow
    best_answer = get_google(query)
    # get_stackoverflow(query)

    if "stackoverflow" in best_answer:
        _parse_so_top_answer(best_answer)
Пример #12
0
def get_google(query,
               n_answers=3,
               best_so=False,
               mute=False,
               break_on_best=False):
    """
    Prints links to the top hits on google given a search query (str)
    and returns a link to the top one.

    :param query: str, search query
    :param n_answers: int, number of max results to get
    :param best_so: bool, False. If true the 'best' result must be from SO
    :param mute: bool, False. If true the it doesn't print the results
    :param break_on_best: bool, False. If true the it stops after finding the best answer
    """
    out = [
        f"[{white}]Links to the top 3 results on [{lightsalmon}]google.com[/{lightsalmon}] for the error:\n"
    ]

    best = None
    for n, j in enumerate(
            gsearch(
                "python " + query,
                tld="co.in",
                num=n_answers,
                stop=n_answers,
                pause=0.3,
            )):
        out.append(f"        [{ls}]{j}[/{ls}]\n")

        if best is None:
            if not best_so or "stackoverflow" in j:
                best = j

                if break_on_best:
                    break

    if not mute:
        console.print(*out, "\n")
    return best
Пример #13
0
def whats_pi():
    """
    Prints a Report with an overview of `pyinspect`.

    """
    # ? Intro
    rep = Report(f"Pynspect", dim=orange, accent=orange)
    rep._type = "Pyinspect info"
    rep.width = 100

    rep.add(
        f"[b {lightorange}]The python package for lazy programmers",
        justify="center",
    )

    # Features summary
    rep.add(f"""
[{salmon}]Don't remember a function's name?[/{salmon}] Use `pyinspect` to look for it. 
[{salmon}]Don't remember what a function does?[/{salmon}] Use `pyinspect` to print its source code directly to your terminal. 
[{salmon}]Can't figure out why you keep getting an error?[/{salmon}] Use `pyinspect`'s fancy tracebacks to figure it out
[{salmon}]Still can't figure it out, but too lazy to google it?[/{salmon}] Use `pyinspect` to print Stack Overflow's top answer for your error message directly to your terminal!
    """)

    # Package / Repo info as a nested panel
    info = NestedPanel(color=mocassin, dim=mocassin)
    _info = dict(
        Author=__author__,
        License=__license__,
        Version=__version__,
        Website=__website__,
    )

    if Github is not None:
        n_stars = Github().get_repo("FedeClaudi/pyinspect").stargazers_count

        _info["Github stars"] = n_stars
    else:
        warn(
            "Could not fetch repo info",
            "Perhaps `PyGithub` is not installed?s",
        )

    for k, v in _info.items():
        info.add(f"[b {gray}]{k}[/b {gray}]: [{orange}]{v}", justify="right")
    rep.add(info, "rich")

    # Features examples
    rep.add("""## Features""", "markdown", style=lightsalmon)

    features = {
        "Look up local variables":
        "pinspect.what()",
        "Search functions by name":
        "pinspect.search(package, function_name)",
        "Print source code to console":
        "pinspect.showme(function)",
        "Enhanced tracebacks":
        "pinspect.install_traceback()",
        "Render [i]Stack Overflow[/i] answers in the terminal":
        'pinspect.ask("How to python?")',
    }

    for txt, code in features.items():
        rep.spacer()
        rep.add(f"[{gray}]" + txt, justify="center")
        rep.add("   " + code, "code")

    rep.spacer()
    rep.add(f"[{lightorange}]... and a bunch of others!")

    rep.spacer(2)
    rep.add(f"[{lightsalmon}]Get in touch at:[/{lightsalmon}] {__website__}")

    console.print(rep)
Пример #14
0
 def showkeys(self):
     console.print(f"{len(self._keys)} keys: ", Pretty(self._keys))
Пример #15
0
def listdir(path, extension=None, sortby=None):
    """
    Prints a nicely formatted table with an overview of files
    in a given directory

    :param path: str or Path object. Path to directory being listed
    :param extension: str. If passed files with that extension are highlighted
    :param sortby: str, default None. How to sort items. If None items
            are sorted alphabetically, if 'ext' or 'extension' items are
            sorted by extension, if 'size' items are sorted by size

    returns a list of files
    """
    def sort_ext(item):
        return item.suffix

    def sort_size(item):
        return item.stat().st_size

    # Check paths
    p = Path(path)
    if not p.is_dir():
        raise ValueError(f"The path passed is not a directory: {path}")

    # Create table
    tb = Table(
        box=None,
        show_lines=None,
        show_edge=None,
        expand=False,
        header_style=f"bold {mocassin}",
    )
    tb.add_column(header="Name")
    tb.add_column(header="Size")

    # Sort items
    if sortby == "extension" or sortby == "ext":
        std = sorted(dir_files(p), key=sort_ext)
    elif sortby == "size":
        std = sorted(dir_files(p), key=sort_size, reverse=True)
    else:
        std = sorted(dir_files(p))

    for fl in std:
        complete_path = str(fl)
        parent = fl.parent.name
        fl = fl.name
        _fl = fl

        # Format file name
        fl = f"[{mocassin}]{fl}"

        if extension is not None and fl.endswith(extension):
            fl = f"[{orange}]{_fl}"
            _col = orange
            _dimcol = orange
        else:
            _col = lightorange
            _dimcol = darkgray

        # Get file size
        size = format_size(os.path.getsize(complete_path)).split(" ")

        tb.add_row(
            f'[link=file://"{complete_path}"][dim]{parent}/[/]' + fl,
            f"[{_col}]" + size[0] + f"[/] [{_dimcol}]" + size[1],
        )

    console.print(f"Files in {path}\n",
                  tb,
                  sep="\n",
                  highlight=ReprHighlighter())

    return std
Пример #16
0
def showme(func):
    """
    Given a pointer to a python function, it prints the code of the function.
    Also works for class methods

    :param func: pointer to a python get_class_that_defined_method
    """
    if isbuiltin(func):
        console.print(
            f'[black on {mocassin}]`showme` currently does not work with builtin functions like "{_name(func)}", sorry. '
        )
        return False
    if not (isfunction(func) or isclass(func) or ismethod(func)):
        # check if it's a class instance
        try:
            func = _class(func)
            getsource(func)  # fails on builtins
            if not isclass(func):
                raise TypeError
        except (AttributeError, TypeError):
            console.print(
                f'[black on {mocassin}]`showme` only accepts functions and classes, not "{_class_name(func)}", sorry. '
            )
            return False

        if isclass(func):
            console.print(
                f"[{mocassin}]The object passed is a class instance, printing source code for the class definition"
            )

    # Print source class
    class_obj = get_class_that_defined_method(func)

    output = []
    if class_obj is not None:
        # showing a class method, also include class initial definition in the output
        output.append(
            f"\n[bold green] Method [yellow]{_name(func)}[/yellow] from class [blue]{_name(class_obj)}[/blue]"
        )

        # get end of class docstring
        doc_end = get_end_of_doc_lineno(class_obj)
        truncated = False

        if doc_end is None:
            doc_end = 2
        elif doc_end > 10:
            doc_end = 10
            truncated = True

        output.extend([
            f"\n[{salmon}] Class definition {'(first 10 lines)' if truncated else ''}:",
            # class definition
            Syntax(
                getsource(class_obj),
                lexer="python",
                line_range=(0, doc_end),
                line_numbers=True,
                theme=DimMonokai,
            ),
            f"\n[bold {salmon}]Method code:",
        ])
    else:
        output.append(
            f"\n[bold]Function [yellow]{_name(func)}[/yellow] from [blue]{_module(func)}[/blue]\n"
        )

    console.print(
        *output,
        Syntax(
            getsource(func),
            lexer="python",
            line_numbers=True,
            theme=Monokai,
        ),
    )

    return True
Пример #17
0
def _what_variable(obj, **kwargs):
    """
    Prints a detailed report of a variable, including
      - name
      - __repr__
      - definition
      - attributes and methods

    Getting the name for builtins is tricky so it finds the
    variable's name by scooping around in the locals stack.
    Then it get's the corresponding locals frame's file
    and in it it looks for the line definition of the variable.
    """
    # Get variable's source
    try:
        # if it's a function or class
        _file = getfile(obj)
        line_no = getsourcelines(obj)[-1]
        name = _name(obj)

    except TypeError:  # doesn't work for builtins
        # Get all locals frames
        locs = [s.frame for s in stack()]
        locs = locs[::-1]  # start from most superficial and ingnore current

        # look for variable in locals stack
        for loc in locs:
            var = [(k, v) for k, v in loc.f_locals.items() if np.all(v == obj)]
            if var:
                name, value = var[0]
                try:
                    _file = loc.f_locals["__file__"]
                except KeyError:
                    while True:
                        loc = loc.f_back
                        if not loc or loc is None:
                            _file = ""

                        if "__file__" in loc.f_locals.keys():
                            _file = loc.f_locals["__file__"]
                            break
                break

        # look for variable definition in the source file
        _got_line = False
        if _file:
            with open(_file, "r") as source:
                for line_no, line in enumerate(source):
                    line = line.replace('"', "'")

                    if name in line and str(value) in line:
                        line_no += 1
                        _got_line = True
                        break  # We got the source line!

            if not _got_line:  # failed to find obj in source code
                _file = ""

    # Create report
    rep = Report(f"Inspecting variable: {name}", accent=salmon)
    rep.width = 150
    rep.add("[dim]Variable content:\n[/]")
    rep.add(Pretty(obj), "rich")
    rep.spacer()

    # add source
    if _file and _file.endswith(".py"):
        rep.add(f"[dim]Defined in:[/] {_file}:[{mocassin}]{line_no}")
        rep.add(
            _file,
            "code file",
            theme=Monokai,
            line_numbers=True,
            line_range=[line_no - 2, line_no + 2],
            highlight_lines=[line_no],
        )
    else:
        rep.add(f"[{orange}]Failed to get source code for variable")
    rep.spacer()

    # add rich inspect
    rep.add(
        Inspect(
            obj,
            help=False,
            methods=True,
            private=True,
            dunder=False,
            sort=True,
            all=False,
        ),
        "rich",
    )

    console.print(rep)
Пример #18
0
 def print(self):
     """
     It prints the panel!
     """
     console.print(self)