Skip to content

Commit ca0b4f4

Browse files
Copilotyihui
andauthored
Address second round: wildcards, auto-PR, upload outputs, cleaner register_methods, formula docs
Agent-Logs-Url: https://github.com/yihui/gglite/sessions/42090aea-3171-40d2-b40b-07840c78eada Co-authored-by: yihui <163582+yihui@users.noreply.github.com>
1 parent 2e5e3ee commit ca0b4f4

6 files changed

Lines changed: 78 additions & 16 deletions

File tree

.Rbuildignore

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
^package-lock\.json$
99
^examples$
1010
^G2$
11-
^tests/test-gglite\.qmd$
12-
^tests/test-gglite\.ipynb$
13-
^tests/test-quarto\.py$
14-
^tests/test-jupyter\.py$
11+
^tests/.*\.qmd$
12+
^tests/.*\.ipynb$
13+
^tests/.*\.py$

.github/copilot-instructions.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,14 @@ g2(mtcars, ~ mpg) # histogram
212212
g2(mtcars, ~ cyl) # histogram (cyl is numeric in mtcars)
213213
```
214214

215+
The formula interface also works for other aesthetic channels by passing a
216+
one-sided formula as a named argument:
217+
```r
218+
g2(iris, Sepal.Length ~ Sepal.Width, color = ~ Species)
219+
g2(mtcars, hp ~ mpg, color = ~ cyl, size = ~ wt)
220+
g2(iris, Sepal.Length ~ Sepal.Width, shape = ~ Species)
221+
```
222+
215223
**Drop explicit marks that can be automatically inferred.** gglite's
216224
`auto_mark()` detects the appropriate mark from the data types. Only specify
217225
a mark explicitly when you need a non-default one:

.github/workflows/test-quarto-jupyter.yml

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ on:
66
workflow_dispatch:
77
push:
88
paths:
9-
- 'tests/test-gglite.qmd'
10-
- 'tests/test-gglite.ipynb'
9+
- 'tests/*.qmd'
10+
- 'tests/*.ipynb'
11+
- 'tests/*.py'
1112
pull_request:
1213
paths:
13-
- 'tests/test-gglite.qmd'
14-
- 'tests/test-gglite.ipynb'
14+
- 'tests/*.qmd'
15+
- 'tests/*.ipynb'
16+
- 'tests/*.py'
1517

16-
permissions: read-all
18+
permissions:
19+
contents: write
20+
pull-requests: write
1721

1822
jobs:
1923
test:
@@ -57,10 +61,36 @@ jobs:
5761
env:
5862
SCREENSHOT_PATH: /tmp/jupyter-test.png
5963

60-
- name: Upload screenshots
64+
- name: Open PR to update notebook if outputs changed (main only)
65+
if: github.ref == 'refs/heads/main'
66+
env:
67+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68+
run: |
69+
python tests/normalize-notebook.py \
70+
/tmp/test-gglite-executed.ipynb tests/test-gglite.ipynb
71+
if git diff --quiet tests/test-gglite.ipynb; then
72+
echo "Notebook outputs unchanged."
73+
else
74+
BRANCH="auto/update-notebook-$(date +%Y%m%d%H%M%S)"
75+
git config user.email "github-actions[bot]@users.noreply.github.com"
76+
git config user.name "github-actions[bot]"
77+
git checkout -b "$BRANCH"
78+
git add tests/test-gglite.ipynb
79+
git commit -m "Update notebook outputs"
80+
git push origin "$BRANCH"
81+
gh pr create \
82+
--title "Update notebook outputs" \
83+
--body "Automated update of notebook cell outputs after execution on main." \
84+
--base main --head "$BRANCH"
85+
fi
86+
87+
- name: Upload artifacts
6188
if: always()
6289
uses: actions/upload-artifact@HEAD
6390
with:
64-
name: browser-screenshots
65-
path: /tmp/*.png
91+
name: test-outputs
92+
path: |
93+
/tmp/*.png
94+
tests/test-gglite.html
95+
/tmp/test-gglite-executed.ipynb
6696

R/render.R

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,14 +385,12 @@ record_print.g2 = function(x, ...) {
385385
}
386386

387387
register_methods = function(pkgs, generics) {
388-
stopifnot(length(pkgs) == length(generics))
389388
for (i in seq_along(pkgs)) local({
390389
pkg = pkgs[[i]]; generic = generics[[i]]
391-
method_name = paste0(generic, '.g2')
392390
hook = function(...) {
393391
registerS3method(
394392
generic, 'g2',
395-
get(method_name, envir = asNamespace('gglite')),
393+
asNamespace('gglite')[[paste0(generic, '.g2')]],
396394
envir = asNamespace(pkg)
397395
)
398396
}

tests/normalize-notebook.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""Normalize and copy an executed Jupyter notebook.
2+
3+
Strips execution metadata (execution counts, timestamps) that change between
4+
runs and copies the normalized notebook to the target path. Used by CI to
5+
produce a clean diff when deciding whether to open a PR.
6+
7+
Usage: python tests/normalize-notebook.py <source.ipynb> <target.ipynb>
8+
"""
9+
import json, sys
10+
11+
12+
def normalize(nb: dict) -> dict:
13+
for cell in nb.get('cells', []):
14+
cell['execution_count'] = None
15+
cell.get('metadata', {}).pop('execution', None)
16+
for out in cell.get('outputs', []):
17+
out.pop('execution_count', None)
18+
return nb
19+
20+
21+
if __name__ == '__main__':
22+
src, dst = sys.argv[1], sys.argv[2]
23+
with open(src) as f:
24+
nb = json.load(f)
25+
with open(dst, 'w') as f:
26+
json.dump(normalize(nb), f, indent=1)
27+
f.write('\n')

tests/testit/test-gglite.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ assert('repr_html.g2 returns complete HTML with CDN and chart', {
502502
(grepl('G2.Chart', html, fixed = TRUE))
503503
})
504504

505-
if (xfun::loadable('repr')) assert('repr methods are registered when repr is loaded', {
505+
assert('repr methods are registered when repr is loaded', {
506506
loadNamespace('repr')
507507
chart = g2(mtcars, x = 'mpg', y = 'hp') |> mark_point()
508508
html = repr::repr_html(chart)

0 commit comments

Comments
 (0)