mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-02 05:30:38 +08:00
Merge branch 'CURA-10901-warn-if-formulas-are-NOK' into yttr
This commit is contained in:
commit
92bed2a185
@ -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
|
||||
|
@ -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)]
|
||||
|
155
printer-linter/src/printerlinter/linters/formulas.py
Normal file
155
printer-linter/src/printerlinter/linters/formulas.py
Normal 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user