Merge branch 'CURA-10901-warn-if-formulas-are-NOK' into yttr

This commit is contained in:
Saumya Jain 2024-04-10 10:56:32 +02:00 committed by GitHub
commit 92bed2a185
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 160 additions and 3 deletions

View File

@ -4,6 +4,7 @@ checks:
diagnostic-definition-redundant-override: true
diagnostic-resources-macos-app-directory-name: true
diagnostic-resource-file-deleted: true
diagnostic-incorrect-formula: true
diagnostic-material-temperature-defined: true
fixes:
diagnostic-definition-redundant-override: true

View File

@ -6,6 +6,7 @@ from .linters.defintion import Definition
from .linters.linter import Linter
from .linters.meshes import Meshes
from .linters.directory import Directory
from .linters.formulas import Formulas
def getLinter(file: Path, settings: dict) -> Optional[List[Linter]]:
@ -14,12 +15,12 @@ def getLinter(file: Path, settings: dict) -> Optional[List[Linter]]:
return [Directory(file, settings)]
if ".inst" in file.suffixes and ".cfg" in file.suffixes:
return [Directory(file, settings), Profile(file, settings)]
return [Directory(file, settings), Profile(file, settings), Formulas(file, settings)]
if ".def" in file.suffixes and ".json" in file.suffixes:
if file.stem in ("fdmprinter.def", "fdmextruder.def"):
return None
return [Directory(file, settings), Definition(file, settings)]
return [Formulas(file, settings)]
return [Directory(file, settings), Definition(file, settings), Formulas(file, settings)]
if file.parent.stem == "meshes":
return [Meshes(file, settings)]

View File

@ -0,0 +1,155 @@
import difflib
import json
import os
import re
from configparser import ConfigParser
from pathlib import Path
from typing import Iterator
from unittest.mock import MagicMock
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.VersionUpgradeManager import VersionUpgradeManager
from cura.CuraApplication import CuraApplication
from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions
from ..diagnostic import Diagnostic
from ..replacement import Replacement
from .linter import Linter
FORMULA_NAMES = [
"extruderValue", "extruderValues", "anyExtruderWithMaterial", "anyExtruderNrWithOrDefault",
"resolveOrValue", "defaultExtruderPosition", "valueFromContainer", "extruderValueFromContainer"
]
DELIMITERS = [r'\+', '-', '=', '/', '\*', r'\(', r'\)', r'\[', r'\]', '{', '}', ' ', '^']
class Formulas(Linter):
"""Finds issues in definition files, such as overriding default parameters."""
def __init__(self, file: Path, settings: dict) -> None:
super().__init__(file, settings)
self._cura_correction_strings = FORMULA_NAMES + list(self.getCuraSettingList())
self._definition = {}
def getCuraSettingList(self) -> list:
if VersionUpgradeManager._VersionUpgradeManager__instance is None:
VersionUpgradeManager._VersionUpgradeManager__instance = VersionUpgradeManager(MagicMock())
CuraApplication._initializeSettingDefinitions()
definition_container = DefinitionContainer("whatever")
with open(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "resources", "definitions", "fdmprinter.def.json"), encoding="utf-8") as data:
definition_container.deserialize(data.read())
return definition_container.getAllKeys()
def check(self) -> Iterator[Diagnostic]:
if self._settings["checks"].get("diagnostic-incorrect-formula", False):
for check in self.checkFormulas():
yield check
yield
def checkFormulas(self) -> Iterator[Diagnostic]:
self._loadDefinitionFiles(self._file)
self._content = self._file.read_text()
definition_name = list(self._definition.keys())[0]
definition = self._definition[definition_name]
if "overrides" in definition:
for key, value_dict in definition["overrides"].items():
for value in value_dict:
if value in ("enable", "resolve", "value", "minimum_value_warning", "maximum_value_warning",
"maximum_value", "minimum_value"):
key_incorrect = self.checkValueIncorrect(key)
if key_incorrect:
found = self._appendCorrections(key, key)
value_incorrect = self.checkValueIncorrect(value_dict[value])
if value_incorrect:
found = self._appendCorrections(key, value_dict[value])
if key_incorrect or value_incorrect:
if len(found.group().splitlines()) > 1:
replacements = []
else:
replacements = [Replacement(
file=self._file,
offset=found.span(1)[0],
length=len(found.group()),
replacement_text=self._replacement_text)]
yield Diagnostic(
file=self._file,
diagnostic_name="diagnostic-incorrect-formula",
message=f"Given formula {found.group()} seems incorrect, Do you mean {self._correct_formula}? please correct the formula and try again.",
level="Error",
offset=found.span(0)[0],
replacements=replacements
)
yield
def _appendCorrections(self, key, incorrectString):
if self._file.suffix == '.cfg':
key_with_incorrectValue = re.compile(r'(\b' + key + r'\b\s*=\s*[^=\n]+.*)')
else:
key_with_incorrectValue = re.compile(r'.*(\"' + key + r'\"[\s\:\S]*?)\{[\s\S]*?\},?')
found = key_with_incorrectValue.search(self._content)
if len(found.group().splitlines()) > 1:
self._replacement_text = ''
else:
self._replacement_text = found.group().replace(incorrectString, self._correct_formula)
return found
def _loadDefinitionFiles(self, definition_file) -> None:
""" Loads definition file contents into self._definition. Also load parent definition if it exists. """
definition_name = Path(definition_file.stem).stem
if not definition_file.exists() or definition_name in self._definition:
return
if definition_file.suffix == ".json":
# Load definition file into dictionary
self._definition[definition_name] = json.loads(definition_file.read_text())
if definition_file.suffix == ".cfg":
self._definition[definition_name] = self._parseCfg(definition_file)
def _parseCfg(self, file_path:Path) -> dict:
config = ConfigParser()
config.read([file_path])
file_data ={}
overrides = {}
available_sections = ["values"]
for section in available_sections:
options = config.options(section)
for option in options:
values ={}
values["value"] = config.get(section, option)
overrides[option] = values
file_data["overrides"]= overrides# Process the value here
return file_data
def checkValueIncorrect(self, formula) -> bool:
if isinstance(formula, str):
self._correct_formula = self._correctTyposInFormula(formula)
if self._correct_formula == formula:
return False
return True
else:
return False
def _correctTyposInFormula(self, formula):
pattern = '|'.join(DELIMITERS)
tokens = re.split(pattern, formula)
output = formula
for token in tokens:
if '(' not in token and ')' not in token:
cleaned_token = re.sub(r'[^\w\s]', '', token)
possible_matches = difflib.get_close_matches(cleaned_token, self._cura_correction_strings, n=1, cutoff=0.8)
if possible_matches:
output = output.replace(cleaned_token, possible_matches[0])
return output