Skip to content

Conversation

colin-nolan
Copy link

@colin-nolan colin-nolan commented Sep 8, 2025

This PR allows standard tracebacks to be enabled using the environment variable: TYPER_STANDARD_TRACEBACK.

The work is a follow up to the discussion with @svlandeg: #1284.

The existing environment variable _TYPER_STANDARD_TRACEBACK is retained but referred to as "deprecated" because:

  • It is non-standard for an advertised env to start with an underscore.
  • Environment variables that start with an underscore have been observed as causing issues in some execution environments (e.g. with AWS Lambda).

Manual testing with:

import typer

def main():
    raise Exception("Hello World")

if __name__ == "__main__":
    typer.run(main)

Produces expected results:

$ python will-fail.py 
╭───────────────────────────────────────── Traceback (most recent call last) ─────────────────────────────────────────╮
│ /Users/cnolan/repositories/typer/will-fail.py:5 in main                                                             │
│                                                                                                                     │
│   2 import typer                                                                                                    │
│   3                                                                                                                 │
│   4 def main():                                                                                                     │
│ ❱ 5 │   raise Exception("Hello World")                                                                              │
│   6                                                                                                                 │
│   7 if __name__ == "__main__":                                                                                      │
│   8 │   typer.run(main)                                                                                             │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Exception: Hello World
$ TYPER_STANDARD_TRACEBACK=1 python will-fail.py 
Traceback (most recent call last):
  File "/Users/cnolan/repositories/typer/will-fail.py", line 8, in <module>
    typer.run(main)
    ~~~~~~~~~^^^^^^
  File "/Users/cnolan/repositories/typer/typer/main.py", line 1073, in run
    app()
    ~~~^^
  File "/Users/cnolan/repositories/typer/typer/main.py", line 332, in __call__
    raise e
  File "/Users/cnolan/repositories/typer/typer/main.py", line 315, in __call__
    return get_command(self)(*args, **kwargs)
           ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/cnolan/repositories/typer/.venv/lib/python3.13/site-packages/click/core.py", line 1442, in __call__
    return self.main(*args, **kwargs)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/cnolan/repositories/typer/typer/core.py", line 700, in main
    return _main(
        self,
    ...<6 lines>...
        **extra,
    )
  File "/Users/cnolan/repositories/typer/typer/core.py", line 193, in _main
    rv = self.invoke(ctx)
  File "/Users/cnolan/repositories/typer/.venv/lib/python3.13/site-packages/click/core.py", line 1226, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cnolan/repositories/typer/.venv/lib/python3.13/site-packages/click/core.py", line 794, in invoke
    return callback(*args, **kwargs)
  File "/Users/cnolan/repositories/typer/typer/main.py", line 692, in wrapper
    return callback(**use_params)
  File "/Users/cnolan/repositories/typer/will-fail.py", line 5, in main
    raise Exception("Hello World")
Exception: Hello World
$ _TYPER_STANDARD_TRACEBACK=1 python will-fail.py 
Traceback (most recent call last):
  File "/Users/cnolan/repositories/typer/will-fail.py", line 8, in <module>
    typer.run(main)
    ~~~~~~~~~^^^^^^
  File "/Users/cnolan/repositories/typer/typer/main.py", line 1073, in run
    app()
    ~~~^^
  File "/Users/cnolan/repositories/typer/typer/main.py", line 332, in __call__
    raise e
  File "/Users/cnolan/repositories/typer/typer/main.py", line 315, in __call__
    return get_command(self)(*args, **kwargs)
           ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/cnolan/repositories/typer/.venv/lib/python3.13/site-packages/click/core.py", line 1442, in __call__
    return self.main(*args, **kwargs)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/cnolan/repositories/typer/typer/core.py", line 700, in main
    return _main(
        self,
    ...<6 lines>...
        **extra,
    )
  File "/Users/cnolan/repositories/typer/typer/core.py", line 193, in _main
    rv = self.invoke(ctx)
  File "/Users/cnolan/repositories/typer/.venv/lib/python3.13/site-packages/click/core.py", line 1226, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cnolan/repositories/typer/.venv/lib/python3.13/site-packages/click/core.py", line 794, in invoke
    return callback(*args, **kwargs)
  File "/Users/cnolan/repositories/typer/typer/main.py", line 692, in wrapper
    return callback(**use_params)
  File "/Users/cnolan/repositories/typer/will-fail.py", line 5, in main
    raise Exception("Hello World")
Exception: Hello World

Keeps support fo `_TYPER_STANDARD_TRACEBACK` but notes as deprecated
because:
- It is non-standard for an advertised env to start with an underscore.
- Environment variables that start with an underscore have been observed as
  causing issues in some execution environments (e.g. with AWS Lambda).

See: fastapi#1284
Copy link
Contributor

github-actions bot commented Sep 8, 2025

@svlandeg svlandeg added the feature New feature, enhancement or request label Sep 9, 2025
@svlandeg svlandeg changed the title Adds support for standard tracebacks via the env TYPER_STANDARD_TRACEBACK ✨ Add support for standard tracebacks via the env TYPER_STANDARD_TRACEBACK Sep 9, 2025
@svlandeg svlandeg self-assigned this Sep 9, 2025
Copy link
Member

@svlandeg svlandeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, this PR adds the option to set an env var TYPER_STANDARD_TRACEBACK while still supporting the old var _TYPER_STANDARD_TRACEBACK too. We need to support both because we don't want to break existing workflows, but that does complicate the code slightly. I think that's an acceptable trade-off to ensure this variable can be properly set in AWS Lambda etc.

standard_traceback = os.getenv("_TYPER_STANDARD_TRACEBACK")
standard_traceback = os.getenv(
"TYPER_STANDARD_TRACEBACK", os.getenv("_TYPER_STANDARD_TRACEBACK")
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth pointing out that this implementation introduces a priority: if both of these variables are set in the environment, then only TYPER_STANDARD_TRACEBACK will be retrieved & used. I think that's fine though, and it would be an extreme niche case if TYPER_STANDARD_TRACEBACK was set to 0 for instance and _TYPER_STANDARD_TRACEBACK to 1 🤷‍♀️

Copy link
Author

@colin-nolan colin-nolan Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same thought. However, I noticed that it's not possible to turn this functionality off by setting the env to any non-empty value:

typer/typer/main.py

Lines 68 to 73 in 0b6a405

standard_traceback = os.getenv("_TYPER_STANDARD_TRACEBACK")
if (
standard_traceback
or not exception_config
or not exception_config.pretty_exceptions_enable
):
(where standard_traceback = "0" -> truthy).

The consequence is that a disagreement can only happen when one variable is set to empty string, and another set to a value. In this case, did the user mean empty string to signal off, or just "unset"?

I think the new env overriding the legacy is acceptable but it's debatable.

I considered changing to support 0 -> off but I didn't want to increase the scope (and it could be argued that it is a breaking change - but it probably more of a bug fix).

@svlandeg
Copy link
Member

svlandeg commented Sep 9, 2025

I will leave this PR for a final review by Tiangolo 🙏

@svlandeg svlandeg removed their assignment Sep 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature, enhancement or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants