diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml
index f8024a34b6a3..621c4f9ebcee 100644
--- a/python/pluginResources/intellij.python.community.impl.xml
+++ b/python/pluginResources/intellij.python.community.impl.xml
@@ -278,6 +278,7 @@
implementation="com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType$Class"/>
+
diff --git a/python/src/com/jetbrains/python/codeInsight/liveTemplates/PyDecoratorTemplatePreprocessor.kt b/python/src/com/jetbrains/python/codeInsight/liveTemplates/PyDecoratorTemplatePreprocessor.kt
new file mode 100644
index 000000000000..d02b76aa29ce
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/liveTemplates/PyDecoratorTemplatePreprocessor.kt
@@ -0,0 +1,31 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.jetbrains.python.codeInsight.liveTemplates
+
+import com.intellij.codeInsight.template.impl.TemplatePreprocessor
+import com.intellij.openapi.application.WriteAction
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiFile
+import com.intellij.util.text.CharArrayUtil
+
+class PyDecoratorTemplatePreprocessor : TemplatePreprocessor {
+ override fun preprocessTemplate(editor: Editor, file: PsiFile, caretOffset: Int, textToInsert: String, templateText: String) {
+ if (!templateText.startsWith('@')) {
+ return
+ }
+
+ val document: Document = editor.getDocument()
+ if (caretOffset <= 0) {
+ return
+ }
+
+ val text: CharSequence = document.charsSequence
+ val checkPos = CharArrayUtil.shiftBackward(text, caretOffset - 1, " \t")
+
+ if (checkPos >= 0 && text[checkPos] == '@') {
+ WriteAction.run {
+ document.deleteString(checkPos, checkPos + 1)
+ }
+ }
+ }
+}
diff --git a/python/testSrc/com/jetbrains/python/codeInsight/liveTemplates/PyLiveTemplatesExpandingTest.java b/python/testSrc/com/jetbrains/python/codeInsight/liveTemplates/PyLiveTemplatesExpandingTest.java
index f4f5d493c90d..46ba063d98c9 100644
--- a/python/testSrc/com/jetbrains/python/codeInsight/liveTemplates/PyLiveTemplatesExpandingTest.java
+++ b/python/testSrc/com/jetbrains/python/codeInsight/liveTemplates/PyLiveTemplatesExpandingTest.java
@@ -85,6 +85,20 @@ public class PyLiveTemplatesExpandingTest extends PyTestCase {
});
}
+ // PY-36230
+ public void testPropertyDecoratorNoDuplicate() {
+ myFixture.configureByText("a.py", "class MyClass:\n @prop");
+ myFixture.type("\t");
+ myFixture.checkResult("class MyClass:\n @property\n def (self):\n return ");
+ }
+
+ // PY-36230
+ public void testPropertyDecoratorNoAtSymbol() {
+ myFixture.configureByText("a.py", "class MyClass:\n prop");
+ myFixture.type("\t");
+ myFixture.checkResult("class MyClass:\n @property\n def (self):\n return ");
+ }
+
// PY-41231
public void testIterableVariableWithTypeAnnotation() {
doMultiFileTest();