Skip to content

Commit c90fad4

Browse files
committed
parser: Log warning if source_constant used but no constants defined
1 parent d0f1942 commit c90fad4

File tree

2 files changed

+26
-8
lines changed

2 files changed

+26
-8
lines changed

parser/snooty/flutter.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import collections.abc
2+
import dataclasses
23
import typing
3-
from typing import cast, Any, Callable, Dict, Tuple, Type, TypeVar, Optional, Union
4+
from typing import cast, Any, Callable, Dict, Set, Tuple, Type, TypeVar, Optional, Union # noqa
45
from typing_extensions import Protocol
56

6-
CACHED_TYPES: Dict[type, Optional[Dict[str, type]]] = {}
7+
# As of Python 3.7, the mypy Field definition is different from Python's version.
8+
# Use an old-fashioned comment until this situation is fixed.
9+
CACHED_TYPES = {} # type: Dict[type, Optional[Dict[str, dataclasses.Field[Any]]]]
710

811

912
class HasAnnotations(Protocol):
@@ -75,6 +78,9 @@ def inner(ty: type, plural: bool, level: int) -> str:
7578
if ty is type(None): # noqa
7679
return 'nothing'
7780

81+
if ty is object or ty is Any:
82+
return 'anything'
83+
7884
level += 1
7985
if level > 4:
8086
# Making nested English clauses understandable is hard. Give up.
@@ -171,22 +177,34 @@ def check_type(ty: Type[C], data: object, ty_module: str = '') -> C:
171177
if isinstance(data, dict) and ty in CACHED_TYPES:
172178
annotations = CACHED_TYPES[ty]
173179
if annotations is None:
174-
annotations = typing.get_type_hints(ty)
180+
annotations = {field.name: field for field in dataclasses.fields(ty)}
175181
CACHED_TYPES[ty] = annotations
176182
result: Dict[str, object] = {}
177183

178184
# Assign missing fields None
185+
missing: Set[str] = set()
179186
for key in annotations:
180187
if key not in data:
181188
data[key] = None
189+
missing.add(key)
182190

183191
# Check field types
184192
for key, value in data.items():
185193
if key not in annotations:
186194
raise LoadUnknownField(ty, data, key)
187195

188-
expected_type = annotations[key]
189-
result[key] = check_type(expected_type, value, ty.__module__)
196+
field = annotations[key]
197+
have_value = False
198+
if key in missing:
199+
# Use the field's default_factory if it's defined
200+
try:
201+
result[key] = field.default_factory() # type: ignore
202+
have_value = True
203+
except TypeError:
204+
pass
205+
206+
if not have_value:
207+
result[key] = check_type(field.type, value, ty.__module__)
190208

191209
output = ty(**result)
192210
start_line = getattr(data, '_start_line', None)

parser/snooty/types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def get_id(self) -> PurePath:
122122
class ProjectConfig:
123123
root: Path
124124
name: str
125-
constants: Optional[Dict[str, object]] = None
125+
constants: Dict[str, object] = field(default_factory=dict)
126126

127127
@classmethod
128128
def open(cls, root: Path) -> Tuple[Path, 'ProjectConfig', List[Diagnostic]]:
@@ -138,7 +138,7 @@ def open(cls, root: Path) -> Tuple[Path, 'ProjectConfig', List[Diagnostic]]:
138138
pass
139139
path = path.parent
140140

141-
return root, cls(root, 'untitled', None), []
141+
return root, cls(root, 'untitled'), []
142142

143143
def render_constants(self) -> Tuple['ProjectConfig', List[Diagnostic]]:
144144
if not self.constants:
@@ -155,7 +155,7 @@ def render_constants(self) -> Tuple['ProjectConfig', List[Diagnostic]]:
155155

156156
def read(self, path: Path) -> Tuple[str, List[Diagnostic]]:
157157
text = path.open().read()
158-
return ProjectConfig.substitute(self.constants, text) if self.constants else text, []
158+
return ProjectConfig.substitute(self.constants, text)
159159

160160
@staticmethod
161161
def substitute(constants: Dict[str, object], source: str) -> Tuple[str, List[Diagnostic]]:

0 commit comments

Comments
 (0)