Skip to content

Decorator @sentry_sdk.trace is not compatible with Click (edge case) #3939

Description

@alexanderilyin

Environment

SaaS (https://sentry.io/)

Steps to Reproduce

Given custom command loader:

class Loader(RichGroup):

    COMMANDS_FOLDER = os.path.join(os.path.dirname(__file__), "commands")

    def list_commands(self, ctx) -> list[str]:
        rv = []
        try:
            for filename in os.listdir(self.COMMANDS_FOLDER):
                if filename.endswith(".py") and filename != "__init__.py":
                    rv.append(filename[:-3])
            rv.sort()
            return rv
        except OSError as e:
            logging.error("Failed to list commands: %s", e)
            return []

    def get_command(self, ctx, name: str) -> click.Command:
        if not name.isalnum():
            raise click.ClickException("Invalid command name")

        ns = {}
        fn = os.path.join(self.COMMANDS_FOLDER, name + ".py")
        logging.debug("Loading %s", fn)

        if not os.path.exists(fn) or not os.path.isfile(fn):
            raise click.ClickException(f"Command '{name}' not found")

        try:
            with open(fn) as f:
                exec(f.read(), ns, ns)
            if "cli" not in ns:
                raise click.ClickException(f"Command '{name}' is invalid: missing 'cli' attribute")
            return ns["cli"]
        except OSError as e:
            logging.exception(e)
            raise click.ClickException(f"Failed to load command '{name}'") from e
        except SyntaxError as e:
            logging.exception(e)
            raise click.ClickException(f"Command '{name}' contains invalid Python code") from e

When using decorator on a command loaded by loader:

@click.command(help="...")
@sentry_sdk.trace
def cli() -> None:
    pass

Workaround

.../lib/python3.10/site-packages/sentry_sdk/utils.py

 # Python 3: methods, functions, classes
 if func_qualname is not None:
-    if hasattr(func, "__module__"):
+    if hasattr(func, "__module__") and func.__module__ is not None:
         func_qualname = func.__module__ + "." + func_qualname
     func_qualname = prefix + func_qualname + suffix

Expected Result

No exception is raised.

Actual Result

Exception is raised:

⬢ [Docker] ❯ pc status
Traceback (most recent call last):
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/bin/pc", line 6, in <module>
    sys.exit(cli())
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/lib/python3.10/site-packages/rich_click/rich_command.py", line 367, in __call__
    return super().__call__(*args, **kwargs)
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/lib/python3.10/site-packages/click/core.py", line 1161, in __call__
    return self.main(*args, **kwargs)
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/lib/python3.10/site-packages/rich_click/rich_command.py", line 152, in main
    rv = self.invoke(ctx)
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/lib/python3.10/site-packages/click/core.py", line 1697, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/lib/python3.10/site-packages/click/core.py", line 1443, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/lib/python3.10/site-packages/click/core.py", line 788, in invoke
    return __callback(*args, **kwargs)
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/lib/python3.10/site-packages/sentry_sdk/tracing_utils.py", line 705, in func_with_tracing
    qualname_from_function(func),
  File "/home/vscode/.cache/pypoetry/virtualenvs/partcad-dev-_yoEiCkE-py3.10/lib/python3.10/site-packages/sentry_sdk/utils.py", line 1505, in qualname_from_function
    func_qualname = func.__module__ + "." + func_qualname
TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'
Sentry is attempting to send 3 pending events
Waiting up to 1 seconds
Press Ctrl-C to quit

Product Area

APIs

Link

No response

DSN

No response

Version

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions