Is decorator Syntax highlighting meant to be applied inconsistently depending on variable type?
Decorators in Python seem to be highlighted differently based on the underlying source of the decorator. Is this intentional? Is my env misconfigured?
Here's a screenshot capturing the inconsistent highlighting I'm experiencing. Line 14 and line 18 represent a decoration of their respective methods, but they are highlighted quite differently, which makes the code seem more complex than it should.
Reproduction Code
class MethodDecorator:
def __init__(self, prefix):
self.prefix = prefix
def __call__(self, func):
def wrapper(*args, **kwargs):
print(f"{self.prefix}: Before {func.__name__}")
result = func(*args, **kwargs)
print(f"{self.prefix}: After {func.__name__}")
return result
return wrapper
def main(my_param_decorator):
@MethodDecorator(">>>")
def hail(name):
print(f"Hail, {name}!")
@my_param_decorator.decorate
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
hail("Bob")
main(MethodDecorator)
Hi @TheNotary, I'm an AI Support assistant here to help with your issue. While the team reviews your request, I wanted to provide some possible tips and documentation that might help you in the meantime.
Similar Issues (FYI only, not solutions)
The following issues may or may not be related to your report. They are provided for reference only, as they may describe similar symptoms, affect the same feature, or address related concerns, but may not be the same issue.
- Semantic highlighting treats decorated methods as functions rather than methods, causing inconsistent colors. https://github.com/microsoft/pylance-release/issues/6921
- Function aliases receive the “variable” token type instead of “function,” leading to mismatched highlighting. https://github.com/microsoft/pylance-release/issues/5785
- Custom syntax‐scope tokens (including decorators) not applied under Pylance’s semantic coloring. https://github.com/microsoft/pylance-release/issues/401
The team will respond to your issue shortly. Please note this is a trial feature and may not be fully accurate. I hope these suggestions are helpful in the meantime. If this comment helped you, please give it a 👍. If the suggestion was not helpful or was incorrect, please give it a 👎. Your feedback helps us improve!
Thanks for the issue. I modified your code, i'm thinking maybe the theme isn't looking at the 'dcorator' modifier on methods.. I think that the 'decorate' callable should be colored as a decorator. I was able to change the color using these settings.
settings.json
"editor.semanticTokenColorCustomizations": {
"enabled": true,
"rules": {
// Exact match for what the inspector shows now:
"method.decorator": { "foreground": "#FF00FF", "fontStyle": "bold" },
// Useful if you also use free functions/classes as decorators:
"function.decorator": { "foreground": "#FF00FF", "fontStyle": "bold" },
"class.decorator": { "foreground": "#FF00FF", "fontStyle": "bold" },
// Catch‑all: anything used as a decorator regardless of base type
"*.decorator": { "foreground": "#FF00FF" }
}
},
"editor.tokenColorCustomizations": {
"textMateRules": [
{
"scope": "entity.name.function.decorator.python",
"settings": {
"foreground": "#FF00FF"
}
},
{
"scope": "meta.function.decorator.python",
"settings": {
"foreground": "#FF00FF"
}
}
]
}
code
from __future__ import annotations
from functools import wraps
from typing import Callable, ParamSpec, TypeVar
P = ParamSpec("P")
R = TypeVar("R")
class MethodDecorator:
def __init__(self, prefix: str) -> None:
self.prefix = prefix
def __call__(self, func: Callable[P, R]) -> Callable[P, R]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"{self.prefix}: Before {func.__name__}")
result = func(*args, **kwargs)
print(f"{self.prefix}: After {func.__name__}")
return result
return wrapper
# Convenience so you can write `@my_param_decorator.decorate`
# (it just delegates to __call__)
def decorate(self, func: Callable[P, R]) -> Callable[P, R]:
return self(func)
def main(my_param_decorator: MethodDecorator) -> None:
@MethodDecorator(">>>")
def hail(name: str) -> None:
print(f"Hail, {name}!")
@my_param_decorator.decorate
def greet(name: str) -> None:
print(f"Hello, {name}!")
greet("Alice")
hail("Bob")
if __name__ == "__main__":
# Pass an instance, not the class
main(MethodDecorator(">>>"))
to look at the token values being returned. vscode has a tool.
I've noted those handy tips, and the theme tweak you did is so much better, ty! This might be worth considering being included in the default theme.
I'm wondering about the coloring of the parameter too, thought. As a human, when I look at everything past the @ symbol, I interpret it "semantically" as a decoration statement (so the colors of the statement should all be close to each other in hue). The theme is still representing the parameter as a parameter. Is it possible to override it's parameter coloring based on it's function in decorating? I see this strike through it knows I want decorators a certain way, but it's marking the parameter theming as of higher importance.