GithubHelp home page GithubHelp logo

prettyerrors's Introduction

pretty-errors

Prettifies Python exception output to make it legible. Install it with

python -m pip install pretty_errors

If you want pretty_errors to be used whenever you run a python script you must add it to your python startup procedure. You can do so easily by running:

python -m pretty_errors

This is the recommended way to use pretty_errors; apart from being simpler and universal, using it will mean SyntaxError exceptions also get formatted prettily (which doesn't work if you are manually importing pretty_errors).


Example


If you have not installed it universally you can use it in your project simply by importing it:

import pretty_errors

Note you need to be running in a terminal capable of colour output in order to get colour output: in Windows this means powershell, cmder, etc. If you must use a monochrome terminal then you can call the helper function pretty_errors.mono(), which will set some config options in a way that is useful for monochrome output.

Monochrome

If you want to configure the output then use pretty_errors.configure(), pretty_errors.whitelist(), pretty_errors.blacklist(), pretty_errors.pathed_config(). For example:

import pretty_errors
pretty_errors.configure(
    separator_character = '*',
    filename_display    = pretty_errors.FILENAME_EXTENDED,
    line_number_first   = True,
    display_link        = True,
    lines_before        = 5,
    lines_after         = 2,
    line_color          = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color,
    code_color          = '  ' + pretty_errors.default_config.line_color,
    truncate_code       = True,
    display_locals      = True
)
pretty_errors.blacklist('c:/python')

Result


Scraping STDERR

Sometimes it will be impossible for pretty_errors to utilize sys.excepthook: for instance, if you are using a framework which installs its own logging (such as uvicorn). In such cases, you can make pretty_errors scrape the output to stderr instead, replacing it with its own. To do so simple call:

pretty_errors.replace_stderr()

Note that this will lose some functionality, since pretty_errors will only have access to what is being output on screen, rather then the entire stack trace. A good API will generally have a way to interact with the exception stack, which will allow for using excepthook: replace_stderr should be the last resort. See this comment for an example


Whitelist / Blacklist:

You may use the functions whitelist(path) and blacklist(path) to add paths which will be necessary (whitelist) or excluded (blacklist). The top frame of the stack is never excluded.


Pathed Configurations

You may set up alternate configurations, which are triggered by the path to the code file of the frame. For example, if you were not interested in the system frames (those under 'c:/python') but did not want to hide them completely by using the blacklist you could do this:

meh = pretty_errors.config.copy()
meh.line_color = meh.code_color = meh.filename_color = meh.function_color = meh.line_number_color = (
    pretty_errors.GREY
)
pretty_errors.pathed_config(meh, 'c:/python')

Environment Variables
  • PYTHON_PRETTY_ERRORS
    You may disable pretty_errors by setting the environment variable PYTHON_PRETTY_ERRORS to 0. i.e. at a command prompt:
set PYTHON_PRETTY_ERRORS=0

Calling pretty_errors.activate() will override this.

If you wish to selectively utilize pretty_errors, then use the above, and in your code perform your calculation to determine whether or not to call pretty_errors.activate().

  • PYTHON_PRETTY_ERRORS_ISATTY_ONLY
    It may be desirable to disable pretty_errors when you are redirecting output to a file (to keep error logs, for instance). If you wish to do so, then setting PYTHON_PRETTY_ERRORS_ISATTY_ONLY to non-zero will cause pretty_errors to check if it is running in an interactive terminal, and only activate if so.
set PYTHON_PRETTY_ERRORS_ISATTY_ONLY=1

Setting this will disable replace_stderr() in the same situations, unless you call it with the force parameter: replace_stderr(force=True).

Calling pretty_errors.activate() will override this.

You may check pretty_errors.terminal_is_interactive to see if the terminal is interactive (pretty_errors sets this by checking sys.stderr.isatty()). You can use this to select a different config. For example:

if not pretty_errors.terminal_is_interactive:
    pretty_errors.mono()

Configuration settings:

Configuration settings are stored in pretty_errors.config, though should be set using pretty_errors.configure(). A reference for the default config is stored in pretty_errors.default_config.

  • name
    Optional field to store config name in.

  • line_length
    Output will be wrapped at this point. If set to 0 (which is the default) it will automatically match your console width.

  • full_line_newline
    Insert a hard newline even if the line is full. If line_length is the same as your console width and this is enabled then you will see double newlines when the line is exactly full, so usually you would only set this if they are different.

  • separator_character
    Character used to create the header line. Hyphen is used by default. If set to None or '' then header will be disabled.

  • display_timestamp
    When enabled a timestamp is written in the traceback header.

  • timestamp_function
    Function called to generate timestamp. Default is time.perf_counter.

  • exception_above
    When enabled the exception is displayed above the stack trace.

  • exception_below
    When enabled the exception is displayed below the stack trace.

  • stack_depth
    The maximum number of entries from the stack trace to display. When 0 will display the entire stack, which is the default.

  • top_first
    When enabled the stack trace will be reversed, displaying the top of the stack first.

  • always_display_bottom
    When enabled (which is the default) the bottom frame of the stack trace will always be displayed.

  • show_suppressed
    When enabled all suppressed exceptions in the stack trace will be shown (typically they are suppressed because an exception above them has replaced them). The normal python behaviour is to hide them.

  • filename_display
    How the filename is displayed: may be pretty_errors.FILENAME_COMPACT, pretty_errors.FILENAME_EXTENDED, or pretty_errors.FILENAME_FULL

  • line_number_first
    When enabled the line number will be displayed first, rather than the filename.

  • display_link
    When enabled a link is written below the error location, which VSCode will allow you to click on.

  • lines_after, lines_before
    How many lines of code to display for the top frame, before and after the line the exception occurred on.

  • trace_lines_after, trace_lines_before
    How many lines of code to display for each other frame in the stack trace, before and after the line the exception occurred on.

  • truncate_code
    When enabled each line of code will be truncated to fit the line length.

  • display_locals
    When enabled, local variables appearing in the top stack frame code will be displayed with their values.

  • display_trace_locals
    When enabled, local variables appearing in other stack frame code will be displayed with their values.

  • truncate_locals
    When enabled the values of displayed local variables will be truncated to fit the line length.

  • display_arrow
    When enabled an arrow will be displayed for syntax errors, pointing at the offending token.

  • arrow_head_character, arrow_tail_character
    Characters used to draw the arrow which points at syntax errors.

  • inner_exception_message
    Message displayed when one exception occurs inside another, between the two exceptions. Default is None, which will simply display the exceptions separated by the header. If you want to emulate the default non-pretty behaviour, use this:

inner_exception_message = pretty_errors.MAGENTA + "\n During handling of the above exception, another exception occurred:\n"

Note that if you use top_first then the order will be reversed, so you should use a message like this instead:

inner_exception_message = pretty_errors.MAGENTA + "\n The above exception occurred during another exception:\n"

  • inner_exception_separator
    Default is False. When set to True a header will be written before the inner_exception_message.

  • prefix
    Text string which is displayed at the top of the report, just below the header.

  • infix
    Text string which is displayed between each frame of the stack.

  • postfix
    Text string which is displayed at the bottom of the exception report.

  • reset_stdout
    When enabled the reset escape sequence will be written to stdout as well as stderr; turn this on if your console is being left with the wrong color.


These color strings will be output before the relevant part of the exception message. You may include non-escape sequence strings if you wish; if you do not have a terminal which supports color output, or simply want to include extra demarcation.

  • header_color
    Escape sequence to set header color.

  • timestamp_color
    Escape sequence to set timestamp color.

  • exception_color
    Escape sequence to set exception color.

  • exception_arg_color
    Escape sequence to set exception arguments color.

  • exception_file_color
    Escape sequence to set color of filenames in exceptions (for example, in a FileNotFoundError).

  • filename_color
    Escape sequence to set filename color.

  • line_number_color
    Escape sequence to set line number color.

  • function_color
    Escape sequence to set function color.

  • link_color
    Escape sequence to set link color.

  • line_color
    Escape sequence to set the color of the line of code which caused the exception.

  • code_color
    Escape sequence to set the color of other displayed lines of code.

  • arrow_head_color, arrow_tail_color
    Escape sequence to set the color of the arrow which points at syntax errors.

  • syntax_error_color
    Escape sequence to set the color of the syntax error token.

  • local_name_color
    Escape sequence to set the color of local variable names.

  • local_value_color
    Escape sequence to set the color of local variable values.

  • local_len_color
    Escape sequence to set the color of local value length when local is truncated.

pretty_errors has some built in escape sequence constants you can use when setting these colors:

  • BLACK
  • GREY
  • RED
  • GREEN
  • YELLOW
  • BLUE
  • MAGENTA
  • CYAN
  • WHITE

For each color there is a matching BRIGHT_ variant (i.e. pretty_errors.BRIGHT_RED), as well as a _BACKGROUND variant to set the background color (i.e. pretty_errors.RED_BACKGROUND).

For example:

pretty_errors.configure(
    line_color = pretty_errors.CYAN_BACKGROUND + pretty_errors.BRIGHT_WHITE
)

Further customization

For the most extensive customization (short of forking the package) you may override the default ExceptionWriter class, allowing you to tailor the output however you wish. Typically you will only need to override the write_ methods.

For example:

class MyExceptionWriter(pretty_errors.ExceptionWriter):
    def write_header(self):
        self.output_text('######## ERROR ########')

pretty_errors.exception_writer = MyExceptionWriter()

Run help(pretty_errors.ExceptionWriter) in the python interpreter for more details.

prettyerrors's People

Contributors

cclauss avatar hyliang96 avatar jordyscript avatar onelivesleft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

prettyerrors's Issues

pretty_errors not working without explicit import.

Hi,
I ran sudo python -m pretty_errors and tried installing both sitecustomize.py and pretty_errors.pth (not at the same time) but, pretty_errors does not seem to work.

When installing either of the files, I installed them at /usr/local/lib/python3.8/dist-packages.

With "sitecustomize.py" it simply does not have any effect.
With "pretty_errors.pth" it shows error

Error processing line 2 of /usr/local/lib/python3.8/dist-packages/pretty_errors.pth:

  Traceback (most recent call last):
    File "/usr/lib/python3.8/site.py", line 175, in addpackage
      exec(line)
    File "<string>", line 1, in <module>
    File "/usr/local/lib/python3.8/dist-packages/pretty_errors/__init__.py", line 1, in <module>
      import sys, re, colorama, os, time, linecache
  ModuleNotFoundError: No module named 'colorama'

Even though, Colorama is installed and I the latest version. How do I solve this issue?

Update README to remove reference to "-m pretty_errors" for Python3

Hey @onelivesleft, amazing product! Really makes a difference. Got it to work by following the setup instructions but one thing that happened is that I was being prompted for initial setup over and over when I'm on python3 (3.9.7) and trying to run the package as a -m module. I got the following prompts until I replaced python3 -m pretty_errors file.py with just python3 file.py. Does it make sense to update the README to wok with python3?

pretty_errors found in:
/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/sitecustomize.py

/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/pretty_errors.pth

/Users/yegor.ius/Library/Python/3.9/lib/python/site-packages/usercustomize.py

/Users/yegor.ius/Library/Python/3.9/lib/python/site-packages/pretty_errors.pth


To have pretty_errors be used when you run any python file you may add it to your usercustomize.py (user level) or sitecustomize.py (system level), or to pretty_errors.pth.

(just hit <enter> to accept the defaults if you are unsure)


 Choose folder to install into:

C: Clean startup files (do so before uninstalling pretty_errors)
1: /usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages
2: /Users/yegor.ius/Library/Python/3.9/lib/python/site-packages
0: Exit

Option: [default: 0] 1

 Choose file to install into:

C: Clean startup files (do so before uninstalling pretty_errors)
1: sitecustomize.py
2: pretty_errors.pth
0: Exit

Option: [default: 1] 1

--------------

pretty_errors already present in:

/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/sitecustomize.py

Edit it to set config options.

JupyterLab support

How hard would it be to make this work in JupyterLab?

I tried it, but the regular JupyterLab exception style remains. I guess they do something similar where they intercept the output and have their own format. Any ideas how to make it you pretty_errors?

stack trace when exception occurs in concurrent.futures.ThreadPoolExecutor

The stack trace pretty errors outputs is that of the main thread. Say I have a task in a thread pool that raises an AssertionError. pretty errors just outputs "AssertionError".

runpy.py 196 _run_module_as_main
"/Users/me/.asdf/installs/python/3.10.0/lib/python3.10/runpy.py", line 196
return _run_code(code, main_globals, None,



ap_rescheduler.py 179 reschedule_aps
"xxx.py", line 179
new_schedule = future.result()


_base.py 438 result
"/Users/me/.asdf/installs/python/3.10.0/lib/python3.10/concurrent/futures/_base.py", line 438
return self.__get_result()



_base.py 390 __get_result
"/Users/me/.asdf/installs/python/3.10.0/lib/python3.10/concurrent/futures/_base.py", line 390
try:
    raise self._exception
finally:

self: None

AssertionError

Disabling pretty errors temporarily with PYTHON_PRETTY_ERRORS=0 shows a better trace in that it shows me where the assertion failed.

concurrent.futures.process._RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/Users/me/.asdf/installs/python/3.10.0/lib/python3.10/concurrent/futures/process.py", line 243, in _process_worker
    r = call_item.fn(*call_item.args, **call_item.kwargs)
  File "foo.py", line 463, in bar
    assert False
AssertionError

Is there a setting or trick to enable this while using pretty errors? Thank you.

Uninstall pretty_errors

Hi,

When I'm trying to uninstall pretty_errors after using:
python -m pretty_errors

And the python can't run normally, and got this:
Python can't import pretty_errors.

File path unclickable in pycharm

Default exception stack
image

and when using pretty_errors
image

code:

import pretty_errors
pretty_errors.configure(
    # separator_character = '*',
    # filename_display    = pretty_errors.FILENAME_EXTENDED,
    line_number_first   = True,
    display_link        = True,
    lines_before        = 5,
    lines_after         = 2,
    line_color          = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color,
    code_color          = '  ' + pretty_errors.default_config.line_color,
    # truncate_code       = True,
    # display_locals      = True
)

def run():
    def run1():
        def __run__2():
            raise ValueError()

        __run__2()
    run1()

run()

Is there any config option to make the shown path clickable?

Backslash related failure in virtualenv in Windows

When I try to use this in Windows, in a virtualenv, I get:

runpy.py 194 _run_module_as_main
return _run_code(code, main_globals, None,

runpy.py 87 _run_code
exec(code, run_globals)

__main__.py 133 <module>
found = find_install(True)

__main__.py 66 find_install
for path in getallsitepackages():

__main__.py 54 getallsitepackages
return getsitepackages() + getusersitepackages()

__main__.py 42 getsitepackages
pattern = re.compile(r'^%s$' % os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages'))

re.py 252 compile
return _compile(pattern, flags)

re.py 304 _compile
p = sre_compile.compile(pattern, flags)

sre_compile.py 764 compile
p = sre_parse.parse(p, flags)

sre_parse.py 948 parse
p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)

sre_parse.py 443 _parse_sub
itemsappend(_parse(source, state, verbose, nested + 1,

sre_parse.py 525 _parse
code = _escape(source, this, state)

sre_parse.py 381 _escape
raise source.error("incomplete escape %s" % escape, len(escape))

re.error:
incomplete escape \U at position 3

The culprit is this line in main.py:

pattern = re.compile(r'^%s$' % os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages'))

On Windows, the VIRTUAL_ENV variable will likely be a value beginning with "C:\Users". This triggers the \U error above - it is treating it as an escape code. I would have thought the 'r' in the string would have handled this issue, but apparently it doesn't.

I fixed it via:

            pattern_string = r'^%s$' % os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages')
            pattern_string = pattern_string.replace('\\', '\\\\')
            pattern = re.compile(pattern_string)

This will work in Linux as well as long as the path doesn't have a literal backslash anywhere. I looked for a more "proper" Python solution but did not find any.

line_length default of 0 causes division by zero exception unless overridden

I'm not sure why line_length isn't being changed from it's default of 0, but it's not in my aiohttp/docker container setup.

If I pass line_length=79 into configure, the exception goes away.

Maybe 0 isn't a good default? Or the modulo needs better protection.

handlers.py 236 middleware_handler2021-08-19T16:49:59.121Z ERROR MainProcess:MainThread aiohttp.server:355 Error handling request
Traceback (most recent call last):
  File "/app/server/handlers.py", line 236, in middleware_handler
    response = await handler(request)
  File "/app/server/handlers.py", line 419, in list_patient_workers
    raise Exception("oops. There was a problem.")
Exception: oops. There was a problem.

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/usr/local/lib/python3.6/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/usr/local/lib/python3.6/site-packages/aiohttp/web_middlewares.py", line 119, in impl
    return await handler(request)
  File "/app/server/handlers.py", line 264, in middleware_handler
    pretty_errors.excepthook(*sys.exc_info())
  File "/usr/local/lib/python3.6/site-packages/pretty_errors/__init__.py", line 738, in excepthook
    writer.write_location(code.co_filename, traceback.tb_lineno, code.co_name)
  File "/usr/local/lib/python3.6/site-packages/pretty_errors/__init__.py", line 491, in write_location
    self.config.function_color,    function
  File "/usr/local/lib/python3.6/site-packages/pretty_errors/__init__.py", line 428, in output_text
    if count == 0 or count % line_length != 0 or self.config.full_line_newline:
ZeroDivisionError: integer division or modulo by zero

May need better integration with ASGI/WSGI servers

Hello there,
I'd like to use pretty errors in my web development, but I have some issues.

I use the fastapi framework, which runs multi processes through the ASGI uvicorn server. Doesn't matter what I do (import, setup config, usercustomize.py etc.) errors do not get pretty in this context. They do when I run my normal scripts.

Can I ask for help/directions?

Suboptimal Reporting of Recursion Errors

It appears that the full stack gets printed? Not ideal...

import pretty_errors    # or omit out to see base python behavior

def recursive_function():
    recursive_function()

recursive_function()    # Call the recursive function to trigger the recursion error

Thanks for your hard work on the package! I'll be recommending it to everyone I code with!

Implementation into pytest

Hello there,

Thanks for creating this awesome lib :). I've been using it for few hours and I just cannot get it to work with pytest.

Example code:

import pretty_errors
pretty_errors.replace_stderr()
def test_abc():
    assert 1==1
    a = ''
    print("ok")
    b = a[0]

Terminal output:
image

Is there anyway to make it work with pytest? Thanks a lot :).

Links in powershell/PyCharm

Hi,

When I had display_link=True, I see the link in the console, but I can't click it inside Pycharm.
I'm on powershell, no idea if it work on another shell.

Without pretty errors, clickable:
clickable

With pretty errors, not clickable:
not-clickable

Also, not related, is it planned to have an environment variable for all config parameters? It would be usefull for a general pretty errors install on the system and not having to import pretty errors in the code.

Use *.pth file instead of sitecustomize.py

The site-packages/sitecustomize.py file is not reliable loaded for everybody. To ensure pretty_errors is loaded after python -m pretty_errors, create a site-packages/pretty_errors.pth file containing:

import pretty_errors; pretty_errors.configure() # if you add option here, keep it on one line

If you don't want to use a *.pth, at least use /home/user/.local/lib/python3.7/site-packages/usercustomize.py, which is more likely to be imported and is meant for things like this. But *customize.py files are not imported in venv, which makes them pretty useless for real life Python usage.

PrettyErrors doesn't work with Hydra

Hydra is framework for ellegantly configuring complex applications.
It seems like PrettyErrors simply doesn't do anything when Hydra is used.

import hydra

@hydra.main(config_path="", config_name="config.yaml")
def main(config):
    raise Exception("Some exception")
    
if __name__ == "__main__":
    main()

(if we just comment out the hydra.main decorator then stack trace is printed correctly)

I made and issue about it on Hydra repository: facebookresearch/hydra#1431
It might have to do something with Hydra changing the working directory by default.

Is there any way to make PrettyErrors compatible with Hydra?

Missing import time in usercustomize.py

When trying to change configs globally by uncommenting the pretty_errors.configure( in usercustomize.py, an error is thrown since time is not imported, but is used in timestamp_function = time.perf_counter.

On my end, I've just added import time to the top of the file; however, when generating the file import time should be added as well.

"incomplete escape \U at position 3" when running "python -m pretty_errors"

Hi,

I followed the commands given on the Pypi page.

python -m pip install pretty_errors run fine, but python -m pretty_errors give me this error:

 > python -m pretty_errors

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

runpy.py 196 _run_module_as_main
return _run_code(code, main_globals, None,

runpy.py 86 _run_code
exec(code, run_globals)

__main__.py 136 <module>
found = find_install(True)

__main__.py 69 find_install
for path in getallsitepackages():

__main__.py 57 getallsitepackages
return getsitepackages() + getusersitepackages()

__main__.py 43 getsitepackages
pattern1 = re.compile(r'^%s$' % sep.join([os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages']))

re.py 251 compile
return _compile(pattern, flags)

re.py 303 _compile
p = sre_compile.compile(pattern, flags)

sre_compile.py 764 compile
p = sre_parse.parse(p, flags)

sre_parse.py 948 parse
p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)

sre_parse.py 443 _parse_sub
itemsappend(_parse(source, state, verbose, nested + 1,

sre_parse.py 525 _parse
code = _escape(source, this, state)

sre_parse.py 381 _escape
raise source.error("incomplete escape %s" % escape, len(escape))

re.error:
incomplete escape \U at position 3

Any idea of why (an error on my part or a bug) ?

Environnement:
Windows 10
Python 3.10
pip 21.3.1

Tried on powershell

在win10的powershell中用python3.7时,会报错File "C:\Users\MrL\Anaconda3\lib\site-packages\pretty_errors\__init__.py", line 495, in write_code lines.append(line.rstrip()) AttributeError: 'int' object has no attribute 'rstrip'

在win10的powershell中用python3.7时,会报错如下:

Error in sys.excepthook:
Traceback (most recent call last):
  File "C:\Users\MrL\Anaconda3\lib\site-packages\pretty_errors\__init__.py", line 679, in excepthook
    code_string = writer.write_code(code.co_filename, traceback.tb_lineno, frame.f_globals, count == final)
  File "C:\Users\MrL\Anaconda3\lib\site-packages\pretty_errors\__init__.py", line 495, in write_code
    lines.append(line.rstrip())
AttributeError: 'int' object has no attribute 'rstrip'

将lines.append(line.rstrip())替换成lines.append(str(line).rstrip())之后,使用正常

Only last Exception shown

While dealing with multiple exceptions ("During handling of the above exception, another exception occurred: ..."), Pretty Errors will show only the last one.

Is there a way to show all of them?

Running pretty_errors as script with virtualenv does not find install folders

I'm on Ubuntu 20.04 and attempting to install pretty_errors using the python -m pretty_errors -s -p command within a virtualenv.
Upon running the command, I get the following output:

To have pretty_errors be used when you run any python file you may add it to your usercustomize.py (user level) 
or sitecustomize.py (system level), or to pretty_errors.pth.

(just hit <enter> to accept the defaults if you are unsure)


 Choose folder to install into:

0: Exit

Option: [default: 0]

After which I hit enter and the install fails.
Investigating the __main__.py file along with Issue #38 lead me to the following function on line 41

def getsitepackages():
    pattern1 = re.compile(r'^%s$' % re.escape(os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages')))
    pattern2 = re.compile(r'^%s$' % re.escape(os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'site-packages')))
    paths = [path for path in set(sys.path) if pattern1.search(path) or pattern2.search(path)]
    return paths

Using some basic debugging, I discovered that the paths variable returned was completely empty, despite the fact that pattern1 should have match my virtualenv path: /home/username/.virtualenvs/general/lib/python3.9/site-packages
I traced the source of this error to the call to re.escape in pattern1. The os.path.join call returns /home/username/.virtualenvs/general/lib/python[0-9.]+/site-packages as expected, but the call to re.escape returns the string '/home/username/\\.virtualenvs/general/lib/python\\[0\\-9\\.\\]\\+/site\\-packages'. Because re.escape escapes all regex special characters, the python[0-9]+ portion of the regex gets mangled and causes pattern1.search to fail. Omitting the call to re.escape matches the string as expected. Upon removing the call to re.escape and running python -m pretty_errors -s -p again, everything installed and functioned as expected.

>>> import os, sys, re
>>> sys.path
['', '/usr/lib/python39.zip', '/usr/lib/python3.9', '/usr/lib/python3.9/lib-dynload', '/home/username/.virtualenvs/general/lib/python3.9/site-packages']
>>> venv_path = sys.path[-1]
>>> os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages')
'/home/username/.virtualenvs/general/lib/python[0-9.]+/site-packages'
>>> pattern1_escape = re.compile(r'^%s$' % re.escape(os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages')))
>>> pattern1_escape.search(venv_path)
>>> pattern1_no_escape = re.compile(r'^%s$' % os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages'))
>>> pattern1_no_escape.search(venv_path)
<re.Match object; span=(0, 61), match='/home/username/.virtualenvs/general/lib/python3.9/s>
>>> pattern1_no_escape.search(venv_path).group(0)
'/home/username/.virtualenvs/general/lib/python3.9/site-packages'

Here is my pyvenv.cfg file for Python and virtualenv info:

home = /usr
implementation = CPython
version_info = 3.9.5.final.0
virtualenv = 20.4.6
include-system-site-packages = false
base-prefix = /usr
base-exec-prefix = /usr
base-executable = /usr/bin/python3.9

"python -m pretty_errors" should use the venv when run from one

Currently python -m pretty_errors adds the setup code into the local site-packages dir. A good thing if you use it at the OS level, but in a venv want to install it into the virtualenv site-package, otherwise we can't use pretty_errors.

Be warned that *customize.py files don't work in venv. Check this ticket for an alternative using a "*.pth" files=: #4

Show function arguments?

It seems like there is a display_locals which will show local variables. This is very useful, but often functions are short and the exception can be easily understood by looking at the arguments passed to the function. Is there a setting to display function arguments as well as local variables?

[Question] Can I change PrettyErrors' configuration globally?

Or do I have to add
pretty_errors.configure( separator_character = '*', filename_display = pretty_errors.FILENAME_EXTENDED, line_number_first = True, display_link = True, lines_before = 5, lines_after = 2, line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color, code_color = ' ' + pretty_errors.default_config.line_color, truncate_code = True, display_locals = True )
at the beginning of each file.

Jupyter & Vscode

I cant seem to get this to work with Jupyter and Vscode. is there something special I need to do?

SyntaxError messages just say 'invalid syntax' instead of the provided hints

Issue

When printing a syntax error, the exception includes a help message in the msg attribute, but the message in args[0] is printed instead, which is almost always "invalid syntax" and unhelpful.

Currently:
image

Ideally :
image

Suggested Fix :

Changing line 682 in init.py from exception_value.args = [exception_value.args[0]] to exception_value.args = [exception_value.msg]

Not clickable output

In VSCode default python errors can be clicked and bring the editor will jump to the error line. This doesn't work with pretty errors.
Could this feature be implemented?

Django Compatibility?

Has anyone got this lib working with Django, esp when using a virtualenv? I haven't had any luck yet and I don't really know where to start.

Error using global install script with venv

Attempting the global install via python -m pretty_errors under a virtual environment with path C:\venvs\flask results in this trace:

runpy.py 193 _run_module_as_main
"main", mod_spec)

runpy.py 85 _run_code
exec(code, run_globals)

main.py 133
found = find_install(True)

main.py 66 find_install
for path in getallsitepackages():

main.py 54 getallsitepackages
return getsitepackages() + getusersitepackages()

main.py 42 getsitepackages
pattern = re.compile(r'^%s$' % os.path.join(os.environ['VIRTUAL_ENV'], 'lib', 'python[0-9.]+', 'site-packages'))re.py 233 compile
return _compile(pattern, flags)

re.py 301 _compile
p = sre_compile.compile(pattern, flags)

sre_compile.py 562 compile
p = sre_parse.parse(p, flags)

sre_parse.py 855 parse
p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)

sre_parse.py 416 _parse_sub
not nested and not items))

sre_parse.py 502 _parse
code = _escape(source, this, state)

sre_parse.py 401 _escape
raise source.error("bad escape %s" % escape, len(escape))

sre_constants.error:
bad escape \l at position 15

I attempted to fix by using the method described in another issue, changing regex pattern to allow backslashes in path. But then there is no option to install anywhere:

(flask) C:\Users\dkornacki\rtc-workspace\iSeries Python\iSeries Python\square_po_import>python -m pretty_errors

To have pretty_errors be used when you run any python file you may add it to your usercustomize.py (user level)
or sitecustomize.py (system level), or to pretty_errors.pth.

(just hit to accept the defaults if you are unsure)

Choose folder to install into:

0: Exit

Option: [default: 0]

Templating for the stack trace

There is a limit to what configure() can offer.

It could be nice for it to be able to accept 2 strings, used as template with format() to produce the stack trace. One for the entire stack trace, and one for a one stack entry.

E.G:

pretty_errors.configure(
stack_entry_template="""{directory}{filename}:{line_numer} {function_name}
{code_line}
"""
)

Make defaults minimally invasive

Currently in addition to highlighting the error messages, pretty_errors will attempt to make them more concise by removing information in default traceback messages. This is a suggestion, and not necessarily a problem with the package, but it causes confusion when you don't expect this package to be imported.

The issue I had was torchmetrics imported this package, which patched the sys.execpthook, which left me very confused as to where the filepaths in my traceback went: Lightning-AI/torchmetrics#2544

If this package just highlighted the errors instead of removing the paths to the files they occurred in, then I would have loved the new unexpected behavior.

Because you need to already need have context in order to understand which files the filename-only method refers to perhaps getting that more concise set of errors should be an opt-in configuration? I.e. be minimally invasive by default and let the user choose if they want more complex behavior?

Global configuration file

I may want to centralize the configuration of this lib into one file at the user level, instead of one file per interpreter/venv. It would be nice to have it being able to load the configuration from an ini/toml file in my user config dir.

appdirs can help make it in a clean way: https://pypi.org/project/appdirs/

No folders to install into are found when run in a virtual environment

This issue is the same as #39, unclear why it resurfaced.

When pretty_errors is run as a module inside a pyenv virtualenv to add it to Python startup procedure (python -m pretty_errors), no folders to install into are found.

Updating the regexes should resolve this.

As a workaround I hardcoded the path to site-packages:

vim $(pyenv prefix)/lib/python3.9/site-packages/pretty_errors/__main__.py

# set paths in line 50 to
            # paths = ['<$(pyenv prefix)>/lib/python3.9/site-packages']

[Question] Can PrettyErrors handle pdb's output?

Is it possible to have PrettyErrors handle exception outputs when using Python's pdb?

I've used the following code to import PrettyErrors

import pretty_errors
pretty_errors.replace_stderr() 

but when adding breakpoints within the code the output is not being interpreted.
E.g when running python -m pdb -c continue myscript.py

Colors in sublime text

Hello,

The colours in the error output do not appear in sublime text 3, while if I run the same code in the terminal they do appear.

Output error example in sublime text:
Screenshot from 2020-11-15 15-27-59

Thank you !

FileNotFoundError and similar exceptions doesn't show the files

On FileNotFoundError and similar exceptions, for which it is normally essential to know which path produces the error, PrettyErrors hides the details of the exception.

Looking into the source, there doesn't seem to be an option to enable the full text output of all exceptions.

Example (with default settings of PrettyPrint):

python3
Python 3.8.2 (default, Apr 27 2020, 15:53:34) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('nofile') as file:
...     file.read()
... 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
<stdin> 1 <module>
1

FileNotFoundError:
2
No such file or directory
>>> 

The same example with the default exception output:

PYTHON_PRETTY_ERRORS=0 python3
Python 3.8.2 (default, Apr 27 2020, 15:53:34) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('nofile') as file:
...     file.read()
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'nofile'
>>> 

Issue seems to be that PrettyErrors generalizes exception printing to what is in exception.args. But as OSError or FileNotFoundError show, there is more important detail, which can be retrieved in a generalized way probably only through str(exception).

So I'm wondering, should there be a configurable option to do that? Such as (not tested, just sketching):

def write_exception(self, exception_type, exception_value):
    if exception_value and len(exception_value.args) > 0:
        if self.config.full_exception_message:
            self.output_text([
                self.config.exception_color, self.exception_name(exception_type), ': ',
                self.config.exception_arg_color, str(exception_value), '\n'
            ])
        else:
            self.output_text([
                self.config.exception_color, self.exception_name(exception_type), ':\n',
                self.config.exception_arg_color, '\n'.join((str(x) for x in exception_value.args))
            ])
    else:
        self.output_text([self.config.exception_color, self.exception_name(exception_type)])

What do you think?

Add custom separator text and

Is that possible to add a custom message between the separator like this?
It will be much better if we cand add separator at the end too.

======================= ERROR & STACK =========================
25 <module> demo.py
"/Users/brikerman/Desktop/book/intro_to_tf2.0_code/demo.py", line 25
> a.hello()

20 hello demo.py
"/Users/brikerman/Desktop/book/intro_to_tf2.0_code/demo.py", line 20
  def hello(self):
      a = 1 + 1
>     print(a[1])
  

self: <__main__.Demo object at 0x10779fb70>
a: 2

TypeError:
'int' object is not subscriptable
======================= ERROR & STACK =========================

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.