Skip to content

Commit 7588813

Browse files
authored
Merge pull request github#14864 from github/redsun82/swift-qldocs
Swift: generate more QLdocs
2 parents d056706 + ff529c3 commit 7588813

File tree

342 files changed

+1823
-345
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

342 files changed

+1823
-345
lines changed

misc/codegen/generators/qlgen.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,33 @@ def _get_stub(cls: schema.Class, base_import: str, generated_import_prefix: str)
312312
else:
313313
accessors = []
314314
return ql.Stub(name=cls.name, base_import=base_import, import_prefix=generated_import_prefix,
315-
synth_accessors=accessors, ql_internal="ql_internal" in cls.pragmas)
315+
doc=cls.doc, synth_accessors=accessors,
316+
ql_internal="ql_internal" in cls.pragmas)
317+
318+
319+
_stub_qldoc_header = "// the following QLdoc is generated: if you need to edit it, do it in the schema file\n"
320+
321+
_class_qldoc_re = re.compile(
322+
rf"(?P<qldoc>(?:{re.escape(_stub_qldoc_header)})?/\*\*.*?\*/\s*|^\s*)class\s+(?P<class>\w+)",
323+
re.MULTILINE | re.DOTALL)
324+
325+
326+
def _patch_class_qldoc(cls: str, qldoc: str, stub_file: pathlib.Path):
327+
""" Replace or insert `qldoc` as the QLdoc of class `cls` in `stub_file` """
328+
if not qldoc or not stub_file.exists():
329+
return
330+
qldoc = "\n".join(l.rstrip() for l in qldoc.splitlines())
331+
with open(stub_file) as input:
332+
contents = input.read()
333+
for match in _class_qldoc_re.finditer(contents):
334+
if match["class"] == cls:
335+
qldoc_start, qldoc_end = match.span("qldoc")
336+
contents = f"{contents[:qldoc_start]}{_stub_qldoc_header}{qldoc}\n{contents[qldoc_end:]}"
337+
tmp = stub_file.with_suffix(f"{stub_file.suffix}.bkp")
338+
with open(tmp, "w") as out:
339+
out.write(contents)
340+
tmp.rename(stub_file)
341+
return
316342

317343

318344
def generate(opts, renderer):
@@ -362,9 +388,13 @@ def generate(opts, renderer):
362388
for c in data.classes.values():
363389
path = _get_path(c)
364390
stub_file = stub_out / path
391+
base_import = get_import(out / path, opts.root_dir)
392+
stub = _get_stub(c, base_import, generated_import_prefix)
365393
if not renderer.is_customized_stub(stub_file):
366-
base_import = get_import(out / path, opts.root_dir)
367-
renderer.render(_get_stub(c, base_import, generated_import_prefix), stub_file)
394+
renderer.render(stub, stub_file)
395+
else:
396+
qldoc = renderer.render_str(stub, template='ql_stub_class_qldoc')
397+
_patch_class_qldoc(c.name, qldoc, stub_file)
368398

369399
# for example path/to/elements -> path/to/elements.qll
370400
renderer.render(ql.ImportList([i for name, i in imports.items() if not classes[name].ql_internal]),

misc/codegen/lib/ql.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,6 @@ def has_children(self) -> bool:
141141
def last_base(self) -> str:
142142
return self.bases[-1].base if self.bases else ""
143143

144-
@property
145-
def has_doc(self) -> bool:
146-
return bool(self.doc) or self.ql_internal
147-
148144

149145
@dataclass
150146
class SynthUnderlyingAccessor:
@@ -167,11 +163,16 @@ class Stub:
167163
import_prefix: str
168164
synth_accessors: List[SynthUnderlyingAccessor] = field(default_factory=list)
169165
ql_internal: bool = False
166+
doc: List[str] = field(default_factory=list)
170167

171168
@property
172169
def has_synth_accessors(self) -> bool:
173170
return bool(self.synth_accessors)
174171

172+
@property
173+
def has_qldoc(self) -> bool:
174+
return bool(self.doc) or self.ql_internal
175+
175176

176177
@dataclass
177178
class DbClasses:

misc/codegen/lib/render.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(self, generator: pathlib.Path):
2929
self._r = pystache.Renderer(search_dirs=str(paths.templates_dir), escape=lambda u: u)
3030
self._generator = generator
3131

32-
def render(self, data: object, output: pathlib.Path):
32+
def render(self, data: object, output: typing.Optional[pathlib.Path], template: typing.Optional[str] = None):
3333
""" Render `data` to `output`.
3434
3535
`data` must have a `template` attribute denoting which template to use from the template directory.
@@ -42,13 +42,17 @@ def render(self, data: object, output: pathlib.Path):
4242
extensions = getattr(data, "extensions", [None])
4343
for ext in extensions:
4444
output_filename = output
45-
template = data.template
45+
template_to_use = template or data.template
4646
if ext:
4747
output_filename = output_filename.with_suffix(f".{ext}")
48-
template += f"_{ext}"
49-
contents = self._r.render_name(template, data, generator=self._generator)
48+
template_to_use += f"_{ext}"
49+
contents = self.render_str(data, template_to_use)
5050
self._do_write(mnemonic, contents, output_filename)
5151

52+
def render_str(self, data: object, template: typing.Optional[str] = None):
53+
template = template or data.template
54+
return self._r.render_name(template, data, generator=self._generator)
55+
5256
def _do_write(self, mnemonic: str, contents: str, output: pathlib.Path):
5357
with open(output, "w") as out:
5458
out.write(contents)

misc/codegen/templates/ql_class.mustache

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ private import {{import_prefix}}.Raw
1010
import {{.}}
1111
{{/imports}}
1212

13+
/**
14+
* INTERNAL: This module contains the fully generated definition of `{{name}}` and should not
15+
* be referenced directly.
16+
*/
1317
module Generated {
1418
/**
1519
{{#doc}}

misc/codegen/templates/ql_stub.mustache

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
// generated by {{generator}}, remove this comment if you wish to edit this file
2-
/**
3-
* This module provides a hand-modifiable wrapper around the generated class `{{name}}`.
4-
{{#ql_internal}}
5-
* INTERNAL: Do not use.
6-
{{/ql_internal}}
7-
*/
2+
{{>ql_stub_module_qldoc}}
83
private import {{base_import}}
94
{{#has_synth_accessors}}
105
private import {{import_prefix}}.Raw
116
private import {{import_prefix}}.Synth
127
{{/has_synth_accessors}}
138

14-
{{#ql_internal}}
15-
/**
16-
* INTERNAL: Do not use.
17-
*/
18-
{{/ql_internal}}
9+
{{>ql_stub_class_qldoc}}
1910
class {{name}} extends Generated::{{name}} {
2011
{{#synth_accessors}}
2112
private
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{{#has_qldoc}}
2+
/**
3+
{{#doc}}
4+
* {{.}}
5+
{{/doc}}
6+
{{#ql_internal}}
7+
* INTERNAL: Do not use.
8+
{{/ql_internal}}
9+
*/
10+
{{/has_qldoc}}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* This module provides a hand-modifiable wrapper around the generated class `{{name}}`.
3+
{{#ql_internal}}
4+
* INTERNAL: Do not use.
5+
{{/ql_internal}}
6+
*/

misc/codegen/test/test_ql.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ def test_class_with_children():
155155
([], True, True),
156156
])
157157
def test_has_doc(doc, ql_internal, expected):
158-
cls = ql.Class("Class", doc=doc, ql_internal=ql_internal)
159-
assert cls.has_doc is expected
158+
stub = ql.Stub("Class", base_import="foo", import_prefix="bar", doc=doc, ql_internal=ql_internal)
159+
assert stub.has_qldoc is expected
160160

161161

162162
def test_property_with_description():

misc/codegen/test/test_qlgen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ def test_class_with_doc(generate_classes):
414414
assert generate_classes([
415415
schema.Class("A", doc=doc),
416416
]) == {
417-
"A.qll": (a_ql_stub(name="A"), a_ql_class(name="A", final=True, doc=doc)),
417+
"A.qll": (a_ql_stub(name="A", doc=doc), a_ql_class(name="A", final=True, doc=doc)),
418418
}
419419

420420

misc/codegen/test/test_render.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,45 @@ def test_render(pystache_renderer, sut):
7676
]
7777

7878

79+
def test_render_provided_template(pystache_renderer, sut):
80+
data = mock.Mock(spec=())
81+
custom_template = object()
82+
text = "some text"
83+
pystache_renderer.render_name.side_effect = (text,)
84+
output = paths.root_dir / "a/some/output.txt"
85+
sut.render(data, output, template=custom_template)
86+
87+
assert_file(output, text)
88+
assert pystache_renderer.mock_calls == [
89+
mock.call.render_name(custom_template, data, generator=generator),
90+
]
91+
92+
93+
def test_render_to_string(pystache_renderer, sut):
94+
data = mock.Mock(spec=("template",))
95+
text = "some text"
96+
pystache_renderer.render_name.side_effect = (text,)
97+
result = sut.render_str(data)
98+
99+
assert result == text
100+
assert pystache_renderer.mock_calls == [
101+
mock.call.render_name(data.template, data, generator=generator),
102+
]
103+
104+
105+
def test_render_to_string_with_provided_template(pystache_renderer, sut):
106+
data = mock.Mock(spec=())
107+
custom_template = object()
108+
text = "some text"
109+
pystache_renderer.render_name.side_effect = (text,)
110+
result = sut.render_str(data, custom_template)
111+
112+
assert result == text
113+
assert pystache_renderer.mock_calls == [
114+
mock.call.render_name(custom_template, data, generator=generator),
115+
]
116+
117+
79118
def test_managed_render(pystache_renderer, sut):
80119
data = mock.Mock(spec=("template",))
81120
text = "some text"

0 commit comments

Comments
 (0)