Skip to content

Commit

Permalink
fix: 1) improved behaviour for Unresolved reference 'snakemake' #511
Browse files Browse the repository at this point in the history
…2) also inject snakemake in #553
  • Loading branch information
iromeo committed Oct 22, 2024
1 parent e470518 commit 67f0cd7
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Released on <not released>
- 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))
- Inject some modules in Snakefile file resolve scope w/o import declaration, e.g. os, sys,.. (see [#553](https://github.com/JetBrains-Research/snakecharm/issues/553)
- Do not warn about unresolved `snakemake` variable in the python files, because they could be used as scripts/wrappers for snakemake rules. (see [#511](https://github.com/JetBrains-Research/snakecharm/issues/511))

### Changed
- TODO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class SmkImplicitPySymbolsProvider(
///////////////////////////////////////
// Implicit requires: e.g. 'os', 'sys'
val resolveContext = fromSdk(project, sdk)
listOf("os", "sys").forEach { moduleName ->
listOf("os", "sys", "snakemake").forEach { moduleName ->
var module = resolveQualifiedName(QualifiedName.fromDottedString(moduleName), resolveContext).filterIsInstance<PyFile>().firstOrNull()
if (module == null) {
module =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ class SmkTypeProvider : PyTypeProviderBase() {
pepFile?.findTopLevelClass("Project")?.getType(TypeEvalContext.codeCompletion(project, pepFile))
}
}

// TODO: TYPE FOR 'snakemake' var in run section & python scripts
else -> null
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.jetbrains.snakecharm.codeInsight.resolve

import com.intellij.psi.util.PsiTreeUtil
import com.jetbrains.python.PythonLanguage
import com.jetbrains.python.psi.PyQualifiedExpression
import com.jetbrains.python.psi.resolve.PyReferenceResolveProvider
import com.jetbrains.python.psi.resolve.RatedResolveResult
Expand All @@ -10,10 +11,12 @@ import com.jetbrains.snakecharm.codeInsight.SmkCodeInsightScope
import com.jetbrains.snakecharm.codeInsight.SmkImplicitPySymbolsProvider
import com.jetbrains.snakecharm.codeInsight.resolve.SmkImplicitPySymbolsResolveProviderCompanion.addSyntheticSymbols
import com.jetbrains.snakecharm.codeInsight.resolve.SmkResolveUtil.RATE_IMPLICIT_SYMBOLS
import com.jetbrains.snakecharm.framework.SmkSupportProjectSettings
import com.jetbrains.snakecharm.lang.SnakemakeLanguageDialect
import com.jetbrains.snakecharm.lang.SnakemakeNames.RUN_SECTION_VARIABLE_JOBID
import com.jetbrains.snakecharm.lang.SnakemakeNames.RUN_SECTION_VARIABLE_RULE
import com.jetbrains.snakecharm.lang.SnakemakeNames.SECTION_THREADS
import com.jetbrains.snakecharm.lang.SnakemakeNames.SMK_VARS_SNAKEMAKE
import com.jetbrains.snakecharm.lang.SnakemakeNames.SMK_VARS_WILDCARDS
import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpoint

Expand All @@ -22,6 +25,20 @@ class SmkImplicitPySymbolsResolveProvider : PyReferenceResolveProvider {
element: PyQualifiedExpression,
context: TypeEvalContext,
): List<RatedResolveResult> {
if (!SmkSupportProjectSettings.getInstance(element.project).snakemakeSupportEnabled) {
return emptyList()
}

if (context.origin?.language == PythonLanguage.getInstance()) {
// only ignore `snakemake`
@Suppress("UnstableApiUsage") val referencedName = element.referencedName

val items = mutableListOf<RatedResolveResult>()
if (referencedName == SMK_VARS_SNAKEMAKE) {
items.add(RatedResolveResult(RATE_IMPLICIT_SYMBOLS, null))
}
return items
}
if (SnakemakeLanguageDialect.isInsideSmkFile(context.origin)) {

val contextScope = SmkCodeInsightScope[element]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ object SnakemakeNames {
const val UNPACK_FUNCTION = "unpack"

// TODO: move to YAML api
const val SMK_VARS_SNAKEMAKE = "snakemake"
const val SMK_VARS_WORKFLOW = "workflow"
const val SMK_VARS_CONFIG = "config"
const val SMK_VARS_CLUSTER_ONFIG = "cluster_config" // TODO seems removed in 8.x
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ 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:
| os |
| sys |
| Path |
| os |
| sys |
| snakemake |
| Path |

Scenario: Complete at top-level (GTE 6.1)
Given a snakemake:6.1 project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,11 @@ Feature: Resolve implicitly imported python names
Then reference should resolve to "<symbol_name>" in "<file>"

Examples:
| ptn | text | symbol_name | file |
| os | os | [SKIP] | os/__init__.pyi |
| sy | sys | [SKIP] | sys.py |
| Pat | Path | Path | pathlib.pyi |
| ptn | text | symbol_name | file |
| os | os | [SKIP] | os/__init__.pyi |
| sy | sys | [SKIP] | sys.py |
| sn | snakemake | [SKIP] | snakemake/__init__.py |
| Pat | Path | Path | pathlib.pyi |

Scenario: Resolve at top-level: shell()
Given a snakemake project
Expand Down Expand Up @@ -422,3 +423,19 @@ Feature: Resolve implicitly imported python names
| rule | input | checkpoints |
| rule | input | config |
| checkpoint | input | rules |
Scenario: Warn about unresolved snakemake variable in run section, behaviour differs from scripts
Given a snakemake project
Given I open a file "foo.smk" with text
"""
rule a:
run:
# this becomes too much and should be migrated into a script directive
path = snakemake.input[0]
"""
And PyUnresolvedReferencesInspection inspection is enabled
Then I expect inspection warning on <input> with message
"""
Cannot find reference 'input' in '__init__.py'
"""
When I check highlighting warnings
11 changes: 11 additions & 0 deletions src/test/resources/features/resolve/py_code_resolve.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,14 @@ Feature: Resolve in python part of snakemake file
"""
When I put the caret at repr
Then reference should resolve to "repr" in "builtins.pyi"


Scenario: Do not warn about unresolved snakemake variable in python scripts and wrappers
Given a snakemake project
Given I open a file "foo.smk" with text
"""
assert snakemake.input.get("sort", "missing") == "missing"
"""
And PyUnresolvedReferencesInspection inspection is enabled
Then I expect no inspection errors
When I check highlighting errors

0 comments on commit 67f0cd7

Please sign in to comment.