Skip to content

Commit

Permalink
feat: see [#537]
Browse files Browse the repository at this point in the history
Support for `update` and `before_update` flags. Update inspection that warns if flag functions from `snakemake.io` is used in a wrong section, added info for all flags up to 8.23.1 version
  • Loading branch information
iromeo committed Oct 17, 2024
1 parent b2c6383 commit a483141
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 65 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ Released on <not released>
- Move snakemake API addition/removal info from JAR into `extra/snakemake_api.yaml` in plugin directory
- Inspection: Warn if snakemake section type or API function isn't supported in the current snakemake project based on a snakemake version (see [#500](https://github.com/JetBrains-Research/snakecharm/issues/500)
- Code completion: For section keywords show 'since' version and deprecation notice in the completion list. Do not suggest already removed keywords (see [#535](https://github.com/JetBrains-Research/snakecharm/issues/535)
-

### Fixed
- Improve parser error message when rule/module is declared with name but lacks ':' (see [#515](https://github.com/JetBrains-Research/snakecharm/issues/515))
- Support for `update` and `before_update` flags. Update inspection that warns if flag functions from `snakemake.io` is used in a wrong section, added info for all flags up to 8.23.1 version (see [#537](https://github.com/JetBrains-Research/snakecharm/issues/537))

### Changed
- TODO
Expand Down
79 changes: 70 additions & 9 deletions snakemake_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,36 @@ defaultVersion: "7.32.4"
# * is_accessible_as_placeholder: False | e.g. in `shell: "{<section>}"` and other sections where placeholders not treated as wildcards
# * placeholders_injection_allowed: False (for function), True (else) | if True, than snakemake expands placeholders before call, e.g. like in 'shell' sections in some flag functions from snakemake.io
# * lambda_args: [] | e.g. lambda args for 'params' and 'input' sections are different
# * limit_to_sections: [] | some snakemake.io flag-functions could be used only in certain sections
# * docs_url: '' | Documentation URL
# =========================================
changelog:

# ---------------------------------------
- version: "8.7.0"
introduced:
- name: "snakemake.ioflags.update"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#updating-existing-output-files
type: "function"
limit_to_sections:
- "output"

- name: "snakemake.ioflags.before_update"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#updating-existing-output-files
type: "function"
limit_to_sections:
- "input"

# ---------------------------------------
- version: "8.2.0"
introduced:
- name: "snakemake.io.from_queue"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#continuously-updated-input
type: "function"
limit_to_sections:
- "output"

# ---------------------------------------
- version: "8.0.0"
removed:
- name: "snakemake.io.dynamic"
Expand Down Expand Up @@ -74,6 +101,11 @@ changelog:
keyword_args_allowed: False
placeholders_injection_allowed: False

- name: "snakemake.io.service"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#service-rules-jobs
type: "function"
limit_to_sections:
- "output"
# ----------------------
- version: "6.15.0"

Expand Down Expand Up @@ -202,6 +234,26 @@ changelog:
type: "function"
advice: "use checkpoints instead."

# ----------------------
- version: "5.1.0"

introduced:
- name: "snakemake.io.report"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/reporting.html#reports
limit_to_sections:
- "output"

# ----------------------
- version: "5.0.0"

introduced:
- name: "snakemake.io.pipe"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#piped-output
limit_to_sections:
- "output"

# ----------------------
- version: "4.8.0"

Expand Down Expand Up @@ -396,55 +448,64 @@ changelog:

- name: "snakemake.io.ancient"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#ignoring-timestamps
placeholders_injection_allowed: True
limit_to_sections:
- "input"

- name: "snakemake.io.protected"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#protected-and-temporary-files
limit_to_sections:
- "output"
- "log"
- "benchmark"

- name: "snakemake.io.directory"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#directories-as-outputs
limit_to_sections:
- "output"

- name: "snakemake.io.report"
- name: "snakemake.io.temp"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#protected-and-temporary-files
limit_to_sections:
- "output"

- name: "snakemake.io.temp"
- name: "snakemake.io.temporary"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#protected-and-temporary-files
limit_to_sections:
- "input"
- "output"

- name: "snakemake.io.touch"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#flag-files
limit_to_sections:
- "output"
- "benchmark"
- "log"

- name: "snakemake.io.pipe"
type: "function"
limit_to_sections:
- "output"

- name: "snakemake.io.unpack"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#input-functions-and-unpack
limit_to_sections:
- "input"

- name: "snakemake.io.repeat"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/tutorial/additional_features.html#benchmarking
limit_to_sections:
- "benchmark"

- name: "snakemake.io.dynamic"
type: "function"
limit_to_sections:
- "output"
- "output"

- name: "snakemake.io.local"
type: "function"
docs_url: https://snakemake.readthedocs.io/en/stable/snakefiles/storage.html#local-input-output-files
limit_to_sections:
- "input"
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ import com.jetbrains.python.psi.resolve.fromSdk
import com.jetbrains.python.psi.resolve.resolveQualifiedName
import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.snakecharm.SnakemakeBundle
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SECTION_ACCESSOR_CLASSES
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SMK_API_VERS_6_1
import com.jetbrains.snakecharm.codeInsight.completion.SmkCompletionUtil
import com.jetbrains.snakecharm.framework.SmkSupportProjectSettings
import com.jetbrains.snakecharm.framework.SmkSupportProjectSettingsListener
import com.jetbrains.snakecharm.lang.SnakemakeNames.SNAKEMAKE_MODULE_NAME_IO
import java.util.*
import javax.swing.SwingUtilities

Expand Down Expand Up @@ -132,7 +134,12 @@ class ImplicitPySymbolsProvider(
///////////////////////////////////////
// E.g. expand, temp, .. from 'snakemake.io'
collectTopLevelMethodsFrom(
"snakemake.io", SmkCodeInsightScope.TOP_LEVEL, usedFiles, sdk, elementsCache
SNAKEMAKE_MODULE_NAME_IO, SmkCodeInsightScope.TOP_LEVEL, usedFiles, sdk, elementsCache
)
progressIndicator?.checkCanceled()
// E.g. flags 'update',.. from 'snakemake.ioflags'
collectTopLevelMethodsFrom(
"snakemake.ioflags", SmkCodeInsightScope.TOP_LEVEL, usedFiles, sdk, elementsCache
)
progressIndicator?.checkCanceled()

Expand Down Expand Up @@ -177,15 +184,12 @@ class ImplicitPySymbolsProvider(
// snakemake.io.Wildcards
// snakemake.io.Resources
collectTopLevelClassesInheretedFrom(
"snakemake.io",
SNAKEMAKE_MODULE_NAME_IO,
"snakemake.io.Namedlist",
SmkCodeInsightScope.RULELIKE_RUN_SECTION, usedFiles, sdk, elementsCache
) { className ->
when (className) {
"InputFiles" -> "input"
"OutputFiles" -> "output"
else -> className.lowercase(Locale.getDefault())
}
) { classFqn ->
val sectionName = SECTION_ACCESSOR_CLASSES[classFqn]
sectionName ?: classFqn.split('.').last().lowercase(Locale.getDefault())
}
progressIndicator?.checkCanceled()

Expand Down Expand Up @@ -467,7 +471,7 @@ class ImplicitPySymbolsProvider(
usedFiles: MutableSet<VirtualFile>,
sdk: Sdk,
elementsCache: MutableList<ImplicitPySymbol>,
className2VarNameFun: (String) -> String
classFqn2VarNameFun: (String) -> String
) {
val pyFiles = collectPyFiles(pyModuleFqn, usedFiles, sdk)

Expand All @@ -484,7 +488,8 @@ class ImplicitPySymbolsProvider(
)

if (parentClassRequirement == null || pyClass.inherits(typeEvalContext, parentClassRequirement)) {
val varName = className2VarNameFun(pyClass.name!!)
val fqn = pyClass.qualifiedName ?: pyClass.name!!
val varName = classFqn2VarNameFun(fqn)
elementsCache.add(
ImplicitPySymbol(
varName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ object SnakemakeAPI {
"snakemake.io.Log" to "log",
"snakemake.io.Resources" to "resources"
)
const val SNAKEMAKE_MODULE_NAME_IO_PY = "io.py"
const val SNAKEMAKE_MODULE_NAME_UTILS_PY = "utils.py"

/**
* Sections that execute external script with access to 'snakemake' object, i.e to 'snakemake.input',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ data class SmkAPIAnnParsingIntroductionRecord(
override val name: String = "",
override val type: String = "",
val advice: String = "",
val docs_url: String = "",

// for sections:
val lambda_args: List<String> = emptyList<String>(),
val section: Boolean = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import com.jetbrains.python.psi.PyCallExpression
import com.jetbrains.python.psi.PyFunction
import com.jetbrains.python.psi.PyStringLiteralExpression
import com.jetbrains.snakecharm.SnakemakeBundle
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SNAKEMAKE_MODULE_NAME_UTILS_PY
import com.jetbrains.snakecharm.framework.SmkSupportProjectSettings
import com.jetbrains.snakecharm.lang.SmkLanguageVersion
import com.jetbrains.snakecharm.lang.SnakemakeNames.SNAKEMAKE_FQN_FUN_MIN_VERSION

class SmkMinVersionWarningInspection : SnakemakeInspection() {
override fun buildVisitor(
Expand All @@ -18,7 +18,6 @@ class SmkMinVersionWarningInspection : SnakemakeInspection() {
session: LocalInspectionToolSession,
) = object : SnakemakeInspectionVisitor(holder, getContext(session)) {

@Suppress("UnstableApiUsage")
override fun visitPyCallExpression(node: PyCallExpression) {
val callee = node.callee
if (callee?.name != "min_version") {
Expand All @@ -28,7 +27,7 @@ class SmkMinVersionWarningInspection : SnakemakeInspection() {
val reference = callee.reference
val resolveResult = reference?.resolve()
val function = resolveResult as? PyFunction ?: return
if (function.name != "min_version" || function.containingFile.name != SNAKEMAKE_MODULE_NAME_UTILS_PY) {
if (function.qualifiedName != SNAKEMAKE_FQN_FUN_MIN_VERSION) {
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package com.jetbrains.snakecharm.inspections.smksl
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiReference
import com.jetbrains.python.extensions.getQName
import com.jetbrains.python.psi.PyClass
import com.jetbrains.snakecharm.SnakemakeBundle
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SECTION_ACCESSOR_CLASSES
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SNAKEMAKE_MODULE_NAME_IO_PY
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPIProjectService
import com.jetbrains.snakecharm.inspections.SnakemakeInspection
import com.jetbrains.snakecharm.inspections.smksl.SmkSLUndeclaredSectionInspectionUtil.checkIsSectionNameUnresolved
import com.jetbrains.snakecharm.inspections.smksl.SmkSLUndeclaredSectionInspectionUtil.isSectionNameOfInterest
import com.jetbrains.snakecharm.lang.SnakemakeNames.SNAKEMAKE_MODULE_NAME_IO
import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpoint
import com.jetbrains.snakecharm.stringLanguage.lang.psi.SmkSLReferenceExpression
import com.jetbrains.snakecharm.stringLanguage.lang.psi.references.SmkSLInitialReference
Expand Down Expand Up @@ -55,8 +56,8 @@ object SmkSLUndeclaredSectionInspectionUtil {
return when (declaration) {
null -> true
is PyClass -> {
// is resolved to io.py
declaration.containingFile.name == SNAKEMAKE_MODULE_NAME_IO_PY
// is resolved to snakemake/io.py
declaration.containingFile.getQName()?.toString() == SNAKEMAKE_MODULE_NAME_IO
|| declaration.qualifiedName in SECTION_ACCESSOR_CLASSES
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,13 @@ object SnakemakeNames {
const val RUN_SECTION_VARIABLE_JOBID = "jobid"

const val SNAKEMAKE_METHOD_MULTIEXT = "multiext"

/**
* Constant that holds the module name for Snakemake input/output operations.
* It is used as part of the code insight and symbol resolution process within the Snakemake plugin.
*
* For more details, refer to the class `com.jetbrains.snakecharm.codeInsight.ImplicitPySymbolsProvider`.
*/
const val SNAKEMAKE_MODULE_NAME_IO = "snakemake.io"
const val SNAKEMAKE_FQN_FUN_MIN_VERSION = "snakemake.utils.min_version"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ Feature: Completion in python part of snakemake file
When I put the caret after foo = 1;
And I invoke autocompletion popup
Then completion list should contain:
| expand |
| temp |
| directory |
| directory |
| protected |
| touch |
| unpack |
| ancient |
| ensure |
| shell |
| config |
| rules |
| input |
| pep |
| expand |
| temp |
| update |
| before_update |
| directory |
| protected |
| touch |
| unpack |
| ancient |
| ensure |
| shell |
| config |
| rules |
| input |
| pep |

Scenario: Complete at top-level (GTE 6.1)
Given a snakemake:6.1 project
Expand Down
Loading

0 comments on commit a483141

Please sign in to comment.