Apply configuration updates (with defaults and backward compatibility) at the dictionary level,

allowing old configurations to be properly updated when resaving
(outdated entries removed/new entries added in re-saved version)
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2025-10-30 01:51:01 +01:00
1 parent 6e26d08d5d
commit e85afc1ea8
2 files changed
+43 -21

No files matched your search

+41 -19
View File
@@ -223,21 +223,44 @@ class ProjectConfig(ToolInclusionDefinition, ToStringMixin):
def rel_path_to_project_yml(cls) -> str:
return os.path.join(SERENA_MANAGED_DIR_NAME, cls.SERENA_DEFAULT_PROJECT_FILE)
@classmethod
def load_commented_map(cls, yml_path: str) -> CommentedMap:
"""
Load the project configuration as a CommentedMap, preserving comments and ensuring
completeness of the configuration by applying default values for missing fields
and backward compatibility adjustments.
:param yml_path: the path to the project.yml file
:return: a CommentedMap representing a full project configuration
"""
data = load_yaml(yml_path, preserve_comments=True)
# apply defaults for new fields
data["languages"] = data.get("languages", [])
data["ignored_paths"] = data.get("ignored_paths", [])
data["excluded_tools"] = data.get("excluded_tools", [])
data["included_optional_tools"] = data.get("included_optional_tools", [])
data["read_only"] = data.get("read_only", False)
data["ignore_all_files_in_gitignore"] = data.get("ignore_all_files_in_gitignore", True)
data["initial_prompt"] = data.get("initial_prompt", "")
data["encoding"] = data.get("encoding", DEFAULT_SOURCE_FILE_ENCODING)
# backward compatibility: handle single "language" field
if len(data["languages"]) == 0 and "language" in data:
data["languages"] = [data["language"]]
if "language" in data:
del data["language"]
return data
@classmethod
def _from_dict(cls, data: dict[str, Any]) -> Self:
"""
Create a ProjectConfig instance from a configuration dictionary
Create a ProjectConfig instance from a (full) configuration dictionary
"""
project_name = data["project_name"]
language_strings = data.get("languages", [])
# backwards compatibility
lang_name_mapping = {"javascript": "typescript"}
if len(language_strings) == 0 and "language" in data:
language_strings = [data["language"]]
languages: list[Language] = []
for language_str in language_strings:
for language_str in data["languages"]:
try:
language_str = language_str.lower()
if language_str in lang_name_mapping:
@@ -248,15 +271,15 @@ class ProjectConfig(ToolInclusionDefinition, ToStringMixin):
raise ValueError(f"Invalid language: {data['language']}.\nValid language_strings are: {[l.value for l in Language]}") from e
return cls(
project_name=project_name,
project_name=data["project_name"],
languages=languages,
ignored_paths=data.get("ignored_paths", []),
excluded_tools=data.get("excluded_tools", []),
included_optional_tools=data.get("included_optional_tools", []),
read_only=data.get("read_only", False),
ignore_all_files_in_gitignore=data.get("ignore_all_files_in_gitignore", True),
initial_prompt=data.get("initial_prompt", ""),
encoding=data.get("encoding", DEFAULT_SOURCE_FILE_ENCODING),
ignored_paths=data["ignored_paths"],
excluded_tools=data["excluded_tools"],
included_optional_tools=data["included_optional_tools"],
read_only=data["read_only"],
ignore_all_files_in_gitignore=data["ignore_all_files_in_gitignore"],
initial_prompt=data["initial_prompt"],
encoding=data["encoding"],
)
def to_yaml_dict(self) -> dict:
@@ -279,8 +302,7 @@ class ProjectConfig(ToolInclusionDefinition, ToStringMixin):
return cls.autogenerate(project_root)
else:
raise FileNotFoundError(f"Project configuration file not found: {yaml_path}")
with open(yaml_path, encoding=SERENA_FILE_ENCODING) as f:
yaml_data = yaml.safe_load(f)
yaml_data = cls.load_commented_map(str(yaml_path))
if "project_name" not in yaml_data:
yaml_data["project_name"] = project_root.name
return cls._from_dict(yaml_data)
+2 -2
View File
@@ -12,7 +12,7 @@ from serena.constants import SERENA_FILE_ENCODING, SERENA_MANAGED_DIR_NAME
from serena.ls_manager import LanguageServerFactory, LanguageServerManager
from serena.text_utils import MatchedConsecutiveLines, search_files
from serena.util.file_system import GitignoreParser, match_path
from serena.util.general import load_yaml, save_yaml
from serena.util.general import save_yaml
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_utils import FileUtils
@@ -116,7 +116,7 @@ class Project(ToStringMixin):
"""
config_path = os.path.join(self.project_root, self.project_config.rel_path_to_project_yml())
log.info("Saving updated project configuration to %s", config_path)
config_with_comments = load_yaml(config_path, preserve_comments=True)
config_with_comments = ProjectConfig.load_commented_map(config_path)
config_with_comments.update(self.project_config.to_yaml_dict())
save_yaml(config_path, config_with_comments, preserve_comments=True)