Skip to content

Commit d0f1942

Browse files
committed
parser: Add source_constants
1 parent cea4bb7 commit d0f1942

File tree

12 files changed

+152
-89
lines changed

12 files changed

+152
-89
lines changed

parser/snooty/flutter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def check_type(ty: Type[C], data: object, ty_module: str = '') -> C:
225225

226226
raise LoadError('Unsupported PEP-484 type', ty, data)
227227

228-
if ty is object or ty is Any:
228+
if ty is object or ty is Any or isinstance(data, ty):
229229
return cast(C, data)
230230

231231
raise LoadError('Unloadable type', ty, data)

parser/snooty/gizaparser/extracts.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dataclasses import dataclass
2-
from pathlib import PurePath
2+
from pathlib import Path
33
from typing import Callable, List, Tuple, Sequence, Optional
44
from ..flutter import checked
55
from .nodes import Inheritable, GizaCategory, HeadingMixin
@@ -37,12 +37,11 @@ def extract_to_page(page: Page,
3737
}
3838

3939

40-
@dataclass
4140
class GizaExtractsCategory(GizaCategory[Extract]):
4241
def parse(self,
43-
path: PurePath,
42+
path: Path,
4443
text: Optional[str] = None) -> Tuple[Sequence[Extract], str, List[Diagnostic]]:
45-
extracts, text, diagnostics = parse(Extract, path, text)
44+
extracts, text, diagnostics = parse(Extract, path, self.project_config, text)
4645

4746
def report_missing_ref(extract: Extract) -> bool:
4847
diagnostics.append(

parser/snooty/gizaparser/nodes.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import logging
44
import re
55
from dataclasses import dataclass, field
6-
from pathlib import PurePath
6+
from pathlib import Path, PurePath
77
from typing import cast, Any, Callable, Dict, Set, Generic, Optional, \
88
TypeVar, Tuple, Iterator, Sequence, List, Union
99
from ..flutter import checked
10-
from ..types import Diagnostic, Page, EmbeddedRstParser, SerializableType
10+
from ..types import Diagnostic, Page, EmbeddedRstParser, SerializableType, ProjectConfig
1111

1212
_T = TypeVar('_T', str, object)
1313
PAT_SUBSTITUTION = re.compile(r'\{\{([\w-]+)\}\}')
@@ -116,7 +116,7 @@ class GizaFile(Generic[_I]):
116116
"""A GizaFile represents a single Giza YAML file."""
117117
__slots__ = ('path', 'text', 'data')
118118

119-
path: PurePath
119+
path: Path
120120
text: str
121121
data: Sequence[_I]
122122

@@ -126,11 +126,12 @@ class GizaCategory(Generic[_I]):
126126
"""A GizaCategory stores metadata about a "category" of Giza YAML files. For
127127
example, "steps", or "apiargs". Each GizaCategory contains all types necessary
128128
to transform a given path into Pages."""
129+
project_config: ProjectConfig
129130
nodes: Dict[str, GizaFile[_I]] = field(default_factory=dict)
130131
dg: DependencyGraph = field(default_factory=DependencyGraph)
131132

132133
def parse(self,
133-
path: PurePath,
134+
path: Path,
134135
text: Optional[str] = None) -> Tuple[Sequence[_I], str, List[Diagnostic]]:
135136
pass
136137

@@ -139,7 +140,7 @@ def to_pages(self,
139140
data: Sequence[_I]) -> List[Page]:
140141
pass
141142

142-
def add(self, path: PurePath, text: str, elements: Sequence[_I]) -> None:
143+
def add(self, path: Path, text: str, elements: Sequence[_I]) -> None:
143144
file_id = path.name
144145
self.nodes[file_id] = GizaFile(path, text, elements)
145146
dependencies = set()

parser/snooty/gizaparser/parse.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
import yaml
33
import yaml.resolver
44
import yaml.scanner
5-
from pathlib import PurePath
5+
from pathlib import Path
66
from typing import List, Optional, Tuple, Type, TypeVar
77
from yaml.composer import Composer
88
from ..flutter import check_type, LoadError, mapping_dict
9-
from ..types import Diagnostic, SerializableType
9+
from ..types import Diagnostic, SerializableType, ProjectConfig
1010

1111
_T = TypeVar('_T')
1212
logger = logging.getLogger(__name__)
@@ -48,23 +48,24 @@ def dict_constructor(loader: yaml.Loader, node: yaml.nodes.Node) -> mapping_dict
4848

4949

5050
def parse(ty: Type[_T],
51-
path: PurePath,
51+
path: Path,
52+
project_config: ProjectConfig,
5253
text: Optional[str] = None) -> Tuple[List[_T], str, List[Diagnostic]]:
54+
diagnostics: List[Diagnostic] = []
5355
if text is None:
54-
with open(path, 'r') as f:
55-
text = f.read()
56+
text, diagnostics = project_config.read(path)
5657

5758
try:
5859
parsed_yaml = load_yaml(text)
5960
except yaml.scanner.ScannerError as err:
6061
lineno = err.problem_mark.line
6162
col = err.problem_mark.column
62-
return [], text, [Diagnostic.error(err.problem, (lineno, col))]
63+
return [], text, diagnostics + [Diagnostic.error(err.problem, (lineno, col))]
6364

6465
try:
6566
parsed = [check_type(ty, data) for data in parsed_yaml]
66-
return parsed, text, []
67+
return parsed, text, diagnostics
6768
except LoadError as err:
6869
mapping = err.bad_data if isinstance(err.bad_data, dict) else {}
6970
lineno = mapping.get('_start_line', 0)
70-
return [], text, [Diagnostic.error(str(err), lineno)]
71+
return [], text, diagnostics + [Diagnostic.error(str(err), lineno)]

parser/snooty/gizaparser/steps.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dataclasses import dataclass
2-
from pathlib import PurePath
2+
from pathlib import Path
33
from typing import Callable, Optional, List, Sequence, Union, Tuple
44
from ..flutter import checked
55
from .parse import parse
@@ -110,12 +110,11 @@ def step_to_page(page: Page, step: Step, rst_parser: EmbeddedRstParser) -> Seria
110110
}
111111

112112

113-
@dataclass
114113
class GizaStepsCategory(GizaCategory[Step]):
115114
def parse(self,
116-
path: PurePath,
115+
path: Path,
117116
text: Optional[str] = None) -> Tuple[Sequence[Step], str, List[Diagnostic]]:
118-
return parse(Step, path, text)
117+
return parse(Step, path, self.project_config, text)
119118

120119
def to_pages(self,
121120
page_factory: Callable[[], Tuple[Page, EmbeddedRstParser]],

parser/snooty/gizaparser/test_extracts.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
from typing import Dict, Tuple, List
33
from .extracts import GizaExtractsCategory
44
from .nodes import ast_to_testing_string
5-
from ..types import Diagnostic, Page, EmbeddedRstParser
5+
from ..types import Diagnostic, Page, EmbeddedRstParser, ProjectConfig
66
from ..parser import make_embedded_rst_parser
77

88

99
def test_extract() -> None:
10-
category = GizaExtractsCategory()
10+
project_config = ProjectConfig(Path('test_data'), '')
11+
category = GizaExtractsCategory(project_config)
1112
path = Path('test_data/extracts-test.yaml')
1213
child_path = Path('test_data/extracts-test-child.yaml')
1314

@@ -35,7 +36,7 @@ def add_child_file() -> List[Diagnostic]:
3536

3637
def create_page() -> Tuple[Page, EmbeddedRstParser]:
3738
page = Page(path, '', {})
38-
return page, make_embedded_rst_parser(path, page, all_diagnostics[path])
39+
return page, make_embedded_rst_parser(project_config, page, all_diagnostics[path])
3940

4041
pages = category.to_pages(create_page, giza_node.data)
4142
assert len(pages) == 4

parser/snooty/language_server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ def set_diagnostics(self, page_path: PurePath, diagnostics: List[types.Diagnosti
209209
'diagnostics': workspace_item.create_lsp_diagnostics()
210210
})
211211

212-
def uri_to_path(self, uri: Uri) -> PurePath:
213-
path = PurePath(uri.replace('file://', '', 1).replace(self.root_uri, ''))
212+
def uri_to_path(self, uri: Uri) -> Path:
213+
path = Path(uri.replace('file://', '', 1).replace(self.root_uri, ''))
214214
self.path_to_uri[path] = uri
215215
return path
216216

parser/snooty/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from pathlib import Path, PurePath
99
from typing import List
1010

11-
from . import language_server
11+
# from . import language_server
1212
from .parser import Project, RST_EXTENSIONS
1313
from .types import Page, Diagnostic
1414

@@ -106,9 +106,9 @@ def usage(exit_code: int) -> None:
106106
def main() -> None:
107107
logging.basicConfig(level=logging.INFO)
108108

109-
if len(sys.argv) == 2 and sys.argv[1] == 'language-server':
110-
language_server.start()
111-
return
109+
# if len(sys.argv) == 2 and sys.argv[1] == 'language-server':
110+
# language_server.start()
111+
# return
112112

113113
if len(sys.argv) not in (3, 4) or sys.argv[1] not in ('watch', 'build'):
114114
usage(1)

parser/snooty/parser.py

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,16 @@
55
import os
66
import pwd
77
import subprocess
8-
from dataclasses import dataclass
98
from functools import partial
109
from pathlib import Path, PurePath
11-
from typing import Any, Dict, Tuple, Optional, Set, List
10+
from typing import Any, Dict, Tuple, Optional, Set, List, Iterable
1211
from typing_extensions import Protocol
1312
import docutils.utils
14-
import toml
1513

1614
from . import gizaparser, rstparser, util
17-
from .flutter import check_type, checked
1815
from .gizaparser.nodes import GizaCategory
19-
from .types import Diagnostic, SerializableType, EmbeddedRstParser, Page, StaticAsset
16+
from .types import Diagnostic, SerializableType, EmbeddedRstParser, Page, \
17+
StaticAsset, ProjectConfigError, ProjectConfig
2018

2119
RST_EXTENSIONS = {'.rst', '.txt'}
2220
logger = logging.getLogger(__name__)
@@ -171,6 +169,9 @@ def add_static_asset(self, path: PurePath) -> StaticAsset:
171169
self.static_assets.add(static_asset)
172170
return static_asset
173171

172+
def add_diagnostics(self, diagnostics: Iterable[Diagnostic]) -> None:
173+
self.diagnostics.extend(diagnostics)
174+
174175
def __make_child_visitor(self) -> 'JSONVisitor':
175176
visitor = type(self)(self.project_root, self.docpath, self.document)
176177
visitor.diagnostics = self.diagnostics
@@ -193,12 +194,9 @@ def dispatch_departure(self, node: docutils.nodes.Node) -> None:
193194

194195

195196
def parse_rst(parser: rstparser.Parser[JSONVisitor],
196-
path: PurePath,
197+
path: Path,
197198
text: Optional[str] = None) -> Tuple[Page, List[Diagnostic]]:
198-
if text is None:
199-
with open(path, 'r') as f:
200-
text = f.read()
201-
visitor = parser.parse(path, text)
199+
visitor, text = parser.parse(path, text)
202200

203201
return Page(
204202
path,
@@ -207,7 +205,7 @@ def parse_rst(parser: rstparser.Parser[JSONVisitor],
207205
visitor.static_assets), visitor.diagnostics
208206

209207

210-
def make_embedded_rst_parser(project_root: Path,
208+
def make_embedded_rst_parser(project_config: ProjectConfig,
211209
page: Page,
212210
diagnostics: List[Diagnostic]) -> EmbeddedRstParser:
213211
def parse_embedded_rst(rst: str,
@@ -216,8 +214,8 @@ def parse_embedded_rst(rst: str,
216214
# Crudely make docutils line numbers match
217215
text = '\n' * lineno + rst.strip()
218216
visitor_class = InlineJSONVisitor if inline else JSONVisitor
219-
parser = rstparser.Parser(project_root, visitor_class)
220-
visitor = parser.parse(page.source_path, text)
217+
parser = rstparser.Parser(project_config, visitor_class)
218+
visitor, _ = parser.parse(page.source_path, text)
221219
children: List[SerializableType] = visitor.state[-1]['children']
222220

223221
diagnostics.extend(visitor.diagnostics)
@@ -242,55 +240,38 @@ def on_update(self, prefix: List[str], page_id: str, page: Page) -> None: ...
242240
def on_delete(self, page_id: str) -> None: ...
243241

244242

245-
@checked
246-
@dataclass
247-
class ProjectConfig:
248-
name: str
249-
250-
@classmethod
251-
def open(cls, root: Path) -> Tuple[Path, 'ProjectConfig']:
252-
path = root
253-
while path.parent != path:
254-
try:
255-
with open(path.joinpath('snooty.toml'), 'r') as f:
256-
return path, check_type(ProjectConfig, toml.load(f))
257-
except FileNotFoundError:
258-
pass
259-
path = path.parent
260-
261-
return root, cls('untitled')
262-
263-
264243
class Project:
265244
def __init__(self,
266245
root: Path,
267246
backend: ProjectBackend) -> None:
268247
root = root.resolve(strict=True)
269-
root, config = ProjectConfig.open(root)
248+
root, self.config, config_diagnostics = ProjectConfig.open(root)
249+
250+
if config_diagnostics:
251+
backend.on_diagnostics(root, config_diagnostics)
252+
raise ProjectConfigError()
270253

271254
self.root = root
272-
self.parser = rstparser.Parser(self.root, JSONVisitor)
255+
self.parser = rstparser.Parser(self.config, JSONVisitor)
273256
self.static_assets: Dict[PurePath, Set[StaticAsset]] = collections.defaultdict(set)
274257
self.backend = backend
275258

276-
self.steps_category = gizaparser.steps.GizaStepsCategory()
277-
self.extracts_category = gizaparser.extracts.GizaExtractsCategory()
278259
self.yaml_mapping: Dict[str, GizaCategory[Any]] = {
279-
'steps': self.steps_category,
280-
'extracts': self.extracts_category
260+
'steps': gizaparser.steps.GizaStepsCategory(self.config),
261+
'extracts': gizaparser.extracts.GizaExtractsCategory(self.config)
281262
}
282263

283264
username = pwd.getpwuid(os.getuid()).pw_name
284265
branch = subprocess.check_output(
285266
['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
286267
encoding='utf-8').strip()
287-
self.prefix = [config.name, username, branch]
268+
self.prefix = [self.config.name, username, branch]
288269

289270
def get_page_id(self, path: PurePath) -> str:
290271
page_id = path.with_suffix('').relative_to(self.root).as_posix()
291272
return '/'.join(self.prefix + [page_id])
292273

293-
def update(self, path: PurePath, optional_text: Optional[str] = None) -> None:
274+
def update(self, path: Path, optional_text: Optional[str] = None) -> None:
294275
diagnostics: Dict[PurePath, List[Diagnostic]] = {path: []}
295276
prefix = get_giza_category(path)
296277
_, ext = os.path.splitext(path)
@@ -302,9 +283,8 @@ def update(self, path: PurePath, optional_text: Optional[str] = None) -> None:
302283
elif ext == '.yaml' and prefix in self.yaml_mapping:
303284
file_id = os.path.basename(path)
304285
giza_category = self.yaml_mapping[prefix]
305-
needs_rebuild = self.steps_category.dg.dependents[file_id].union(
306-
self.extracts_category.dg.dependents[file_id]).union(
307-
set([file_id]))
286+
needs_rebuild = set((file_id)).union(*(
287+
category.dg.dependents[file_id] for category in self.yaml_mapping.values()))
308288
logger.debug('needs_rebuild: %s', ','.join(needs_rebuild))
309289
for file_id in needs_rebuild:
310290
file_diagnostics: List[Diagnostic] = []
@@ -319,7 +299,7 @@ def update(self, path: PurePath, optional_text: Optional[str] = None) -> None:
319299

320300
def create_page() -> Tuple[Page, EmbeddedRstParser]:
321301
page = Page(giza_node.path, text, {})
322-
return page, make_embedded_rst_parser(self.root, page, file_diagnostics)
302+
return page, make_embedded_rst_parser(self.config, page, file_diagnostics)
323303

324304
giza_category.add(path, text, steps)
325305
pages = giza_category.to_pages(create_page, giza_node.data)
@@ -352,7 +332,7 @@ def build(self) -> None:
352332

353333
# Categorize our YAML files
354334
logger.debug('Categorizing YAML files')
355-
categorized: Dict[str, List[PurePath]] = collections.defaultdict(list)
335+
categorized: Dict[str, List[Path]] = collections.defaultdict(list)
356336
for path in util.get_files(self.root, ('.yaml',)):
357337
prefix = get_giza_category(path)
358338
if prefix in self.yaml_mapping:
@@ -373,7 +353,7 @@ def build(self) -> None:
373353
def create_page() -> Tuple[Page, EmbeddedRstParser]:
374354
page = Page(giza_node.path, giza_node.text, {})
375355
return page, make_embedded_rst_parser(
376-
self.root, page, all_yaml_diagnostics.setdefault(giza_node.path, []))
356+
self.config, page, all_yaml_diagnostics.setdefault(giza_node.path, []))
377357

378358
for page in giza_category.to_pages(create_page, giza_node.data):
379359
self.backend.on_update(self.prefix, self.get_page_id(page.get_id()), page)

0 commit comments

Comments
 (0)