Repository: executablebooks/MyST-Parser

Issue:
For v3.0.1: indented directive options no longer recognised
### What version of `myst-parser` are you using?

3.0.0

### What version dependencies are you using?

<details>

```Package                       Version
----------------------------- -----------
ablog                         0.11.8
accessible-pygments           0.0.4
alabaster                     0.7.16
attrs                         23.2.0
Babel                         2.14.0
bcrypt                        4.1.2
beautifulsoup4                4.12.3
bleach                        6.1.0
certifi                       2024.2.2
cffi                          1.16.0
charset-normalizer            3.3.2
colorama                      0.4.6
cryptography                  42.0.5
defusedxml                    0.7.1
docutils                      0.20.1
fancylog                      0.3.0
fastjsonschema                2.19.1
feedgen                       1.0.0
idna                          3.7
imagesize                     1.4.1
invoke                        2.2.0
Jinja2                        3.1.3
jsonschema                    4.21.1
jsonschema-specifications     2023.12.1
jupyter_client                8.6.1
jupyter_core                  5.7.2
jupyterlab_pygments           0.3.0
linkify-it-py                 2.0.3
lxml                          5.2.1
markdown-it-py                3.0.0
MarkupSafe                    2.1.5
mdit-py-plugins               0.4.0
mdurl                         0.1.2
mistune                       3.0.2
myst-parser                   2.0.0
nbclient                      0.10.0
nbconvert                     7.16.3
nbformat                      5.10.4
nbsphinx                      0.9.3
numpydoc                      1.7.0
packaging                     24.0
pandocfilters                 1.5.1
paramiko                      3.4.0
pip                           23.3.1
platformdirs                  4.2.1
pycparser                     2.22
pydata-sphinx-theme           0.15.2
Pygments                      2.17.2
PyNaCl                        1.5.0
python-dateutil               2.9.0.post0
pywin32                       306
PyYAML                        6.0.1
pyzmq                         26.0.2
referencing                   0.35.0
requests                      2.31.0
rich                          13.7.1
rpds-py                       0.18.0
setuptools                    68.2.2
setuptools-scm                8.0.4
simplejson                    3.19.2
six                           1.16.0
snowballstemmer               2.2.0
soupsieve                     2.5
Sphinx                        7.1.2
sphinx-argparse               0.4.0
sphinx-autodoc-typehints      2.0.1
sphinx_design                 0.5.0
sphinx-sitemap                2.5.1
sphinxcontrib-applehelp       1.0.8
sphinxcontrib-devhelp         1.0.6
sphinxcontrib-htmlhelp        2.0.5
sphinxcontrib-jsmath          1.0.1
sphinxcontrib-qthelp          1.0.7
sphinxcontrib-serializinghtml 1.1.10
tabulate                      0.9.0
tinycss2                      1.3.0
tomli                         2.0.1
tornado                       6.4
traitlets                     5.14.3
typing_extensions             4.11.0
uc-micro-py                   1.0.3
urllib3                       2.2.1
watchdog                      4.0.0
webencodings                  0.5.1
wheel                         0.41.2
```
</details>

### What operating system are you using?

Windows

### Describe the Bug

In version 2.0.0 I have the images with content (I use backticks in the real version but use `:::` here for formatting reasons), e.g.:

```
:::{image} /_static/blog_images/neuroblueprint/NeuroBlueprint_project_tree_dark.png
   :align: center
   :class: only-dark
   :width: 650px
:::
```

```
:::{image} /_static/blog_images/neuroblueprint/NeuroBlueprint_project_tree_light.png
   :align: center
   :class: only-light
   :width: 650px
:::
```

but the content is not respected (e.g. image size) and the warning reads:

```
16: WARNING: 'image': Has content, but none permitted [myst.directive_parse]
```

I checked the changelog and couldnt see anything immediately relevant so listed as bug, but maybe this is a new feature I am misunderstanding.



### Expected Behavior

Image formats with content as directed .

### To Reproduce

I believe building with any image content should show the error.

Discussion:
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/925?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) Report
Attention: Patch coverage is `91.66667%` with `1 lines` in your changes are missing coverage. Please review.
> Project coverage is 90.13%. Comparing base [(`446feba`)](https://app.codecov.io/gh/executablebooks/MyST-Parser/commit/446febadcaec172144e31927e935ee34bfdca4e2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) to head [(`9d1b129`)](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/925?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks).

| [Files](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/925?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) | Patch % | Lines |
|---|---|---|
| [myst\_parser/parsers/directives.py](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/925?src=pr&el=tree&filepath=myst_parser%2Fparsers%2Fdirectives.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks#diff-bXlzdF9wYXJzZXIvcGFyc2Vycy9kaXJlY3RpdmVzLnB5) | 91.66% | [1 Missing :warning: ](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/925?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) |

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master     #925   +/-   ##
=======================================
  Coverage   90.13%   90.13%           
=======================================
  Files          24       24           
  Lines        3416     3416           
=======================================
  Hits         3079     3079           
  Misses        337      337           
```

| [Flag](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/925/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) | Coverage Δ | |
|---|---|---|
| [pytests](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/925/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) | `90.13% <91.66%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/925?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks).


Patch:
diff --git a/myst_parser/parsers/directives.py b/myst_parser/parsers/directives.py
index d6a000c..22ff5a6 100644
--- a/myst_parser/parsers/directives.py
+++ b/myst_parser/parsers/directives.py
@@ -175,34 +175,34 @@ def _parse_directive_options(
 
     :returns: (content, options, validation_errors)
     """
-    yaml_block: None | str = None
+    options_block: None | str = None
     if content.startswith("---"):
         line = None if line is None else line + 1
         content = "\n".join(content.splitlines()[1:])
         match = re.search(r"^-{3,}", content, re.MULTILINE)
         if match:
-            yaml_block = content[: match.start()]
+            options_block = content[: match.start()]
             content = content[match.end() + 1 :]  # TODO advance line number
         else:
-            yaml_block = content
+            options_block = content
             content = ""
-        yaml_block = dedent(yaml_block)
-    elif content.startswith(":"):
+        options_block = dedent(options_block)
+    elif content.lstrip().startswith(":"):
         content_lines = content.splitlines()
         yaml_lines = []
         while content_lines:
-            if not content_lines[0].startswith(":"):
+            if not content_lines[0].lstrip().startswith(":"):
                 break
-            yaml_lines.append(content_lines.pop(0)[1:])
-        yaml_block = "\n".join(yaml_lines)
+            yaml_lines.append(content_lines.pop(0).lstrip()[1:])
+        options_block = "\n".join(yaml_lines)
         content = "\n".join(content_lines)
 
-    has_options_block = yaml_block is not None
+    has_options_block = options_block is not None
 
     if as_yaml:
         yaml_errors: list[ParseWarnings] = []
         try:
-            yaml_options = yaml.safe_load(yaml_block or "") or {}
+            yaml_options = yaml.safe_load(options_block or "") or {}
         except (yaml.parser.ParserError, yaml.scanner.ScannerError):
             yaml_options = {}
             yaml_errors.append(
@@ -226,9 +226,9 @@ def _parse_directive_options(
     validation_errors: list[ParseWarnings] = []
 
     options: dict[str, str] = {}
-    if yaml_block is not None:
+    if options_block is not None:
         try:
-            _options, state = options_to_items(yaml_block)
+            _options, state = options_to_items(options_block)
             options = dict(_options)
         except TokenizeError as err:
             return _DirectiveOptions(


Test patch:
diff --git a/tests/test_renderers/fixtures/directive_parsing.txt b/tests/test_renderers/fixtures/directive_parsing.txt
index 3b8cc86..d148094 100644
--- a/tests/test_renderers/fixtures/directive_parsing.txt
+++ b/tests/test_renderers/fixtures/directive_parsing.txt
@@ -258,6 +258,24 @@ error: missing argument
 error: 1 argument(s) required, 0 supplied
 .
 
+indented_options
+.
+```{note}
+  :class: name
+
+body
+```
+.
+arguments: []
+body:
+- body
+content_offset: 2
+options:
+  class:
+  - name
+warnings: []
+.
+
 option_flags_std
 .
 ```{code-block}