Skip to content

Add custom_language_magics option to support user-defined language magics in Markdown notebooks#1492

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/allow-custom-language-magics
Draft

Add custom_language_magics option to support user-defined language magics in Markdown notebooks#1492
Copilot wants to merge 2 commits intomainfrom
copilot/allow-custom-language-magics

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 22, 2026

Markdown code blocks in languages not covered by Jupytext's built-in _JUPYTER_LANGUAGES set were silently kept as markdown cells instead of being converted to code cells with the appropriate %%lang magic. Users with custom magics (e.g. %%jsx) had no way to opt in.

Changes

  • config.py: New custom_language_magics = List(Unicode()) trait on JupytextConfiguration; propagated to format options via set_default_format_options()
  • formats.py: Added custom_language_magics to _VALID_FORMAT_OPTIONS
  • cell_reader.py (MarkdownCellReader):
    • __init__: Extends start_code_re regex to include custom languages (+ uppercase variants)
    • options_to_metadata: Correctly extracts language name for custom languages (bypasses text_to_metadata's built-in language check which would treat unknown names as metadata keys)
    • find_cell_end: Custom language fenced blocks now correctly act as code cell boundaries
  • jupytext.py:
    • reads(): Merges custom_language_magics into the effective magic list passed to set_main_and_cell_language(), so cells receive their %%lang prefix
    • writes(): Auto-detects non-standard %%lang magics in code cells and records them in custom_language_magics notebook metadata — enables correct round-trips without manual configuration
  • cell_to_text.py: BaseCellExporter passes custom_language_magics to cell_language() so %%lang lines are recognized as language markers during export
  • docs/advanced-options.md: New "Custom language magics" section

Usage

# jupytext.toml
custom_language_magics = ["jsx"]
```jsx
const Hello = () => (<b>Hello</b>);

→ converted to a code cell with `%%jsx` magic. Round-trip (`.ipynb` → `.md` → `.ipynb`) is also handled automatically: when writing a notebook containing `%%jsx` cells to Markdown, `custom_language_magics: [jsx]` is recorded in the notebook metadata header.

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>Allow custom language magics</issue_title>
> <issue_description>## Use case
> 
> I want my markdown docs to be executable as notebooks. The majority of code in my docs is in Python, with a few code blocks in other languages. Jupytext converts most of those languages to code cells with magics, which is great. Some languages aren't covered by built-in magics, so I wrote my own, but I can't get Jupytext to use them. Instead, it leaves those code blocks as markdown.
> 
> <table>
> <thead>
> <tr>
> <th width="500px">md:markdown</th>
> <th width="500px">ipynb</th>
> <th>cell type</th>
> <thead>
> <tbody>
> 
> <tr>
> <td  colspan="3">
> Code blocks in languages with built-in magic support are converted to code cells, as they should.
> </td>
> </tr>
> <tr></tr>
> 
> <tr>
> <td>
> 
> ``````markdown
> ```javascript
> alert();
> ```
> ``````
> </td>
> <td>
> 
> ```
> %%javascript
> alert();
> ```
> 
> </td>
> <td>code</td>
> </tr>
> <tr></tr>
> <tr>
> <td  colspan="3">
> Code blocks in languages without built-in magic support are shown as markdown cells. Probably so they don't cause errors.
> </td>
> </tr>
> <tr></tr>
> <tr>
> <td>
> 
> 
> ``````markdown
> ```jsx
> const Hello = () => (<b>Hello</b>);
> ```
> ``````
> 
> </td>
> <td>
> 
> ``````markdown
> ```jsx
> const Hello = () => (<b>Hello</b>);
> ```
> ``````
> 
> </td>
> <td>markdown</td>
> </tr>
> <tr></tr>
> <tr>
> <td  colspan="3">
> However, I wrote custom magics that can run those languages. So I want Jupytext to produce this:
> </td>
> </tr>
> <tr></tr>
> <tr>
> <td>
> 
> 
> ``````markdown
> ```jsx
> const Hello = () => (<b>Hello</b>);
> ```
> ``````
> 
> </td>
> <td>
> 
> ```
> %%jsx
> const Hello = () => (<b>Hello</b>);
> ```
> 
> </td>
> <td>code</td>
> </tr>
> <tr></tr>
> <tr>
> <td  colspan="3">
> 
> NOTE: When converting from **ipynb** to **md:markdown**, it does work like this. But not the other way around.
> 
> </td>
> </tr>
> <tr></tr>
> 
> </tbody>
> </table>
> 
> ## Possible solution
> 
> Add `custom_language_magics` option (or something) that will extend [`_JUPYTER_LANGUAGES`](https://github.com/mwouts/jupytext/blob/e939bf5359be8391ca8ccd4f931385287dc99992/src/jupytext/languages.py#L6). A list of magics would be enough for my use case, but if it mapped magics to languages, you could also use it to decide whether to comment cells out in percent formats.
> 
> ## Related issues
> 
> - mwouts/jupytext#952</issue_description>
> 
> <agent_instructions>Hi copilot, can you add the suggested option `custom_language_magics` to Jupytext and to its documentation? It should be a list of strings that Jupytext will happen to _JUPYTER_LANGUAGES when parsing markdown files back to ipynb.
> 
> When converting an ipynb file to markdown, if a non-standard magic language is found, can we update the `custom_language_magics` option accordingly, just for the current document?</agent_instructions>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> <comment_new><author>@mwouts</author><body>
> Hi @steovd , thank you for suggesting this! Yes that sounds a very nice addition (well done on having your own magics, too!). I am very open to adding this new option as you suggest. Let's see if we or @copilot can get a PR in that direction!</body></comment_new>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes mwouts/jupytext#1491

<!-- START COPILOT CODING AGENT TIPS -->
---

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. [Learn more about Advanced Security.](https://gh.io/cca-advanced-security)

Co-authored-by: mwouts <29915202+mwouts@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for custom language magics in Jupytext Add custom_language_magics option to support user-defined language magics in Markdown notebooks Feb 22, 2026
Copilot AI requested a review from mwouts February 22, 2026 22:16
@steovd
Copy link
Copy Markdown

steovd commented Feb 22, 2026

writes(): Auto-detects non-standard %%lang magics in code cells and records them in custom_language_magics notebook metadata — enables correct round-trips without manual configuration

I didn't check the code, but this doesn't sound safe. You can't assume that every custom cell magic marks a language (e.g. the built-in %%timeit doesn't). IMO this feature should be opt-in, auto-detection is not necessary.

@mwouts
Copy link
Copy Markdown
Owner

mwouts commented Feb 23, 2026

Hi @steovd , I take good note of this.

I did ask for that feature, though, as I prefer that documents round trip well without having to fine tune the configuration (no sure how timeit does in that respect! Will be interesting to check)

I had not time to check the PR either, will do on the next iteration, likely next week end.

Kind regards

@steovd
Copy link
Copy Markdown

steovd commented Feb 23, 2026

You're right. Default behavior doesn't have to work everywhere, as long as it can be configured when needed. Maybe auto-detect new languages when custom_language_magics isn't configured (not counting notebook metadata), and don't when it is?

Here's a practical same-language custom cell magic: %%ipytest. Currently, when converted to py:percent, only the magic line is commented out, and you can pass the .py file to pytest. If it was auto-detected as a language, the tests would also get commented out. And in md:markdown, I'd want python code blocks (for syntax highlighting), not ipytest.

```python
# %%ipytest
def test_foo(): ...
```
```ipytest
def test_foo(): ...
```

More examples of non-language custom cell magics:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants