44from pathlib import Path , PurePath
55from dataclasses import dataclass , field
66import toml
7- from .flutter import checked , check_type
7+ from .flutter import checked , check_type , LoadError
88from typing import Any , Callable , Dict , Set , List , Tuple , Optional , Union , Match
99
1010PAT_VARIABLE = re .compile (r'{\+([\w-]+)\+}' )
@@ -135,26 +135,30 @@ def source_path(self) -> Path:
135135 @classmethod
136136 def open (cls , root : Path ) -> Tuple ['ProjectConfig' , List [Diagnostic ]]:
137137 path = root
138+ diagnostics = []
138139 while path .parent != path :
139140 try :
140141 with path .joinpath ('snooty.toml' ).open (encoding = 'utf-8' ) as f :
141142 data = toml .load (f )
142143 data ['root' ] = path
143- result , diagnostics = check_type (ProjectConfig , data ).render_constants ()
144- return result , diagnostics
144+ result , parsed_diagnostics = check_type (ProjectConfig , data ).render_constants ()
145+ return result , parsed_diagnostics
145146 except FileNotFoundError :
146147 pass
148+ except LoadError as err :
149+ diagnostics .append (Diagnostic .error (str (err ), 0 ))
150+
147151 path = path .parent
148152
149- return cls (root , 'untitled' ), []
153+ return cls (root , 'untitled' ), diagnostics
150154
151155 def render_constants (self ) -> Tuple ['ProjectConfig' , List [Diagnostic ]]:
152156 if not self .constants :
153157 return self , []
154158 constants : Dict [str , object ] = {}
155159 all_diagnostics : List [Diagnostic ] = []
156160 for k , v in self .constants .items ():
157- result , diagnostics = ProjectConfig .substitute (constants , str (v ))
161+ result , diagnostics = self .substitute (str (v ))
158162 all_diagnostics .extend (diagnostics )
159163 constants [k ] = result
160164
@@ -163,19 +167,18 @@ def render_constants(self) -> Tuple['ProjectConfig', List[Diagnostic]]:
163167
164168 def read (self , path : Path ) -> Tuple [str , List [Diagnostic ]]:
165169 text = path .read_text (encoding = 'utf-8' )
166- return ProjectConfig .substitute (self . constants , text )
170+ return self .substitute (text )
167171
168- @staticmethod
169- def substitute (constants : Dict [str , object ], source : str ) -> Tuple [str , List [Diagnostic ]]:
172+ def substitute (self , source : str ) -> Tuple [str , List [Diagnostic ]]:
170173 """Substitute all placeholders within a string."""
171174 diagnostics : List [Diagnostic ] = []
172175
173176 def handle_match (match : Match [str ]) -> str :
174- """Replace a given placeholder match with a value from the Sphinx
177+ """Replace a given placeholder match with a value from the project
175178 configuration. Log a warning if it's not defined."""
176179 variable_name = match .group (1 )
177180 try :
178- return str (constants [variable_name ])
181+ return str (self . constants [variable_name ])
179182 except KeyError :
180183 lineno = source .count ('\n ' , 0 , match .start ())
181184 diagnostics .append (
0 commit comments