-
Notifications
You must be signed in to change notification settings - Fork 8
ENH: Add gated directives to support code-cells for exercises and solutions #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #45 +/- ##
==========================================
+ Coverage 92.98% 94.10% +1.12%
==========================================
Files 6 7 +1
Lines 442 628 +186
==========================================
+ Hits 411 591 +180
- Misses 31 37 +6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
choldgraf
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a pretty reasonable approach to me - I think we should document the "when to use one vs. the other" question strongly, and also make it clear that the functionality between the two is totally equivalent, the only difference is allowing people to nest {code-cell} objects within solution blocks.
Do you anticipate people wanting to have the same thing for exercise directives?
| A gated directive for solution | ||
|
|
||
| .. solution-end:: <exercise-reference> | ||
| :label: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would anybody use :label: :class: etc here? I feel like we shouldn't document this pattern and should probably raise an error if somebody puts config here - it seems simpler to have all configuration in the start part of the directive
EDIT: ah I see you plan to do this :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. Need to change solution-end to a much simpler class. The only complicated factor is that it can contain content so we need some state parsing etc.
| return parent_start, parent_end | ||
|
|
||
| def apply(self): | ||
| for node in self.document.traverse(solution_start_node): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it'd be good to do a quick check first and raise some errors for the following conditions:
-
Accidentally nesting solution start:
```{solution-start} solution1 ``` ```{solution-start} solution2 ``` ```{solution-end} ``` ```{solution-end} ``` -
End solution without start solution
-
Start solution without end solution (you have this one documented in the comments I believe)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. There needs to be a lot more robustness put into the code to check for structural issues and malformed syntax.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @choldgraf that is a good test case 👍
|
This definitely works for me @mmcky . Fits my simple uses cases and the syntax is clean and simple enough to easily remember. Fully in favor 👍 Oops, hit wrong button 😬 -- reopened. |
|
Thanks for comments @jstac @choldgraf (@chrisjsewell via slack). Appreciate it. Next Steps:
There is the possibility to make this more general to extend any directives using |
|
@AakashGfude I just pushed some additional test cases Also the way the
I don't really see a reason to do any further structural testing on the document. This condition seems to be the main issue -- and I think an
|
|
OK I think the only way to integrate proper error checking is to:
Alternatively,
I will try the alternative first as it would be more efficient to check an already formed registry (as part of the parsing step) The current |
|
In a simple exercise solution, the For compound solutions between gated sections, it would be useful if the whole section could be collapsible, perhaps by the following: More generally, can a class be set in the |
|
Regarding supporting code in Generalising support for gated directives would seem to be a good way of doing this, not least because it then opens up a route for other extension developers to easily support high level blocks defined across multiple heterogenous cells. Naively, I'm assuming that at some point the gated directives essentially gives you a |
Thanks @psychemedia I think this is a good idea. The way |
Support for |
|
@chrisjsewell @AakashGfude I have setup the extension to make use of a I want
coupled with a useful error message. Currently the (ebp) ➜ test-gateddirective git:(gated-directive) make html
Running Sphinx v4.4.0
myst v0.15.2: MdParserConfig(renderer='sphinx', commonmark_only=False, enable_extensions=['dollarmath'], dmath_allow_labels=True, dmath_allow_space=True, dmath_allow_digits=True, dmath_double_inline=False, update_mathjax=True, mathjax_classes='tex2jax_process|mathjax_process|math|output_area', disable_syntax=[], url_schemes=['http', 'https', 'mailto', 'ftp'], heading_anchors=None, heading_slug_func=None, html_meta=[], footnote_transition=True, substitutions=[], sub_delimiters=['{', '}'], words_per_minute=200)
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 6 source files that are out of date
updating environment: [new config] 6 added, 0 changed, 0 removed
Executing: errors_1 in: /Users/mmcky/work/executablebooks/sphinx-exercise/tests/books/test-gateddirective
ERROR: The document (errors_1) is missing a solution-end directive
solution-start at line: 20
Extension error:
[sphinx-exercise] An error has occured when parsing gated directives.
Please check warning messages above
make: *** [html] Error 2but only for Should I run a |
|
OK I have just found https://github.com/executablebooks/sphinx-external-toc/blob/96c80db479b9cd926f2d4dad9cc43ee3c007fcb7/sphinx_external_toc/events.py#L69 so maybe that is tested in |
|
|
thanks @psychemedia -- I will double check the |
Yes, that's where I spotted it. Inside a gated solution. I was expecting the content between the gateposts to be in the |
|
nicely spotted thanks @psychemedia the test fixture shows the same -- will fix that.
The <solution_node classes="solution" docname="solution" hidden="False" ids="solution-gated-1" label="solution-gated-1" names="solution-gated-1" serial_number="0" target_label="exercise-1" title="Solution to" type="solution">
<solution_title>
Solution to
<section ids="solution-content">
<paragraph>
This is a solution to Exercise 1
<CellNode cell_type="code" classes="cell">
<CellInputNode classes="cell_input">
<literal_block language="ipython3" xml:space="preserve"> |
|
Presumably, the gated solutions will not be useful in JupyterLab / JupyerLab-MyST contest because of the inability to propagate metadata up the DOM / select parent HTML blocks that define cells; or, at least, not without some sort of extension support, or the availability of CSS |
|
How easy is it to be able to parse a More generally, can the recipe for gated sections be used to easily create other admonition types that also employ gated sections? Eg could gated blocks be easily added to exercise definitions, or other custom defined admonition blocks based on a similar pattern? |
Sadly no - to my knowledge this will need extension support. There is some work going on at mystjs to bring rendering of |
|
OK so it looks like the <section ids="solution-content">
<paragraph>
This is a solution to Exercise 1
<CellNode cell_type="code" classes="cell">
<CellInputNode classes="cell_input">
<literal_block language="ipython3" xml:space="preserve"> |
|
|
@chrisjsewell @AakashGfude I think I figure out a reasonable solution in 701a398 for testing the Exception cases and checking warning messages |
|
@choldgraf @psychemedia @AakashGfude this is working nicely for me now. I'll plan to merge this soon and start work on @choldgraf this extension to the syntax is quite convenient (vs. nesting structures) -- it might be a nice enhancement proposal for any directive down the track. A real world example can be seen here: https://6226e59b74051d4c841f8092--epic-agnesi-957267.netlify.app/functions.html#solutions which uses |
|
I have now converted one of the QuantEcon lectures sites to use The preview is available here: https://6228365c7ce7b477e5575a73--epic-agnesi-957267.netlify.app/intro.html @AakashGfude would you mind to take a quick look and let me know what you think. |

This PR adds support for
gatedexercise and solution directives, which enables:code-cellin myst-nb) to be used. As thecode-cellis at the root level of the document it is executed byjupyter-cacheand follows withjupyter-bookmethodology of supportingcode-celldirectives at the root level of the document in a linear fashion (which is 1-to-1 withjupyter)[exercise,solution]-startand a[exercise,solution]-enddirective by transforming the AST which can be a convenient way to add nested directives without needing to increase the back-tick depthBasic Syntax
The
exercise-startdirective allows for he same options as coreexercisedirective.Example
will be styled as a single solution in the output
documentTechnical Note
Currently the
solutionandexerciseimplementations differ in approach to try different ways of achieving the same thing.exerciseapproach maintains the originalnodetypeexerciseorenumerate_exerciseand instead usesgated=True/Falsemetadata on the class to determine if it is anexercisestart node. The benefit of this approach is that thecontentis more easily merged as you don't need to create anew_nodeand reassemble it (as in the case ofsolutions)solutionapproach uses fully separate wrapper nodessolution-startandsolution-end, however thesolution-startnode needs to be cast into asolutionnode in the transform.Discussions around Syntax
Pro:
solutiondirective so doesn't change any existing behaviourCon:
highlightertools to parse (i.e. vscode extension)similarthing (solutionandsolution-startwithsolution-end). (@mmcky comment: Not sure this is a huge issue as I think users would typically use one or the other. I would probably start using the gated directives as I like how they look (rather than nested structures).Technical Approach
This PR adds in two directives
solution-startandsolution-endwhich are then transformed duringtransformphase of sphinx to migrate any nodes betweenstartandendto be wrapped in a standardsolutionnode. Therefore there are no additionalvisit_anddepart_methods required.solution-endcan be simplified as it currently inherits fromSolutionDirectiveand doesn't require all the additional logic of titles. It just needs to plan asolution-endnode in the tree.