rstcheck / rstcheck-core

Core library behind rstcheck.
http://rstcheck-core.rtfd.io/
MIT License
4 stars 8 forks source link

code blocks without language error with sphinx #3

Open chrisgilmerproj opened 2 years ago

chrisgilmerproj commented 2 years ago

I am trying to debug an issue with this tool and tried running with debug. Here's the command I ran:

# rstcheck -r project/docs/source --debug

The traceback looks like:

multiprocessing.pool.RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/multiprocessing/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
  File "/usr/local/lib/python3.9/multiprocessing/pool.py", line 48, in mapstar
    return list(map(*args))
  File "/usr/local/lib/python3.9/site-packages/rstcheck.py", line 273, in _check_file
    for error in check(contents,
  File "/usr/local/lib/python3.9/site-packages/rstcheck.py", line 184, in check
    docutils.core.publish_string(
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 407, in publish_string
    output, pub = publish_programmatically(
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 665, in publish_programmatically
    output = pub.publish(enable_exit_status=enable_exit_status)
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 217, in publish
    self.document = self.reader.read(self.source, self.parser,
  File "/usr/local/lib/python3.9/site-packages/docutils/readers/__init__.py", line 72, in read
    self.parse()
  File "/usr/local/lib/python3.9/site-packages/docutils/readers/__init__.py", line 78, in parse
    self.parser.parse(self.input, document)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/__init__.py", line 183, in parse
    self.statemachine.run(inputlines, document, inliner=self.inliner)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 170, in run
    results = StateMachineWS.run(self, input_lines, input_offset,
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 3008, in text
    self.section(title.lstrip(), source, style, lineno + 1, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
    newabsoffset = self.nested_parse(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
    state_machine.run(block, input_offset, memo=self.memo,
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 3008, in text
    self.section(title.lstrip(), source, style, lineno + 1, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
    newabsoffset = self.nested_parse(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
    state_machine.run(block, input_offset, memo=self.memo,
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2769, in underline
    self.section(title, source, style, lineno - 1, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
    newabsoffset = self.nested_parse(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
    state_machine.run(block, input_offset, memo=self.memo,
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2342, in explicit_markup
    nodelist, blank_finish = self.explicit_construct(match)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2354, in explicit_construct
    return method(self, expmatch)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2096, in directive
    return self.run_directive(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2146, in run_directive
    result = directive_instance.run()
  File "/usr/local/lib/python3.9/site-packages/sphinx/directives/patches.py", line 180, in run
    node['language'] = self.env.temp_data.get('highlight_language',
  File "/usr/local/lib/python3.9/site-packages/sphinx/util/docutils.py", line 350, in env
    return self.state.document.settings.env
AttributeError: 'Values' object has no attribute 'env'
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/rstcheck", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.9/site-packages/rstcheck.py", line 945, in main
    results = pool.map(
  File "/usr/local/lib/python3.9/multiprocessing/pool.py", line 364, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "/usr/local/lib/python3.9/multiprocessing/pool.py", line 771, in get
    raise self._value
AttributeError: 'Values' object has no attribute 'env'

I am using python 3.9.10:

# python --version
Python 3.9.10

And my set of packages are:

# pip freeze
alabaster==0.7.12
Babel==2.9.1
certifi==2021.10.8
charset-normalizer==2.0.10
docutils==0.17.1
idna==3.3
imagesize==1.3.0
importlib-metadata==4.11.1
Jinja2==3.0.3
MarkupSafe==2.0.1
packaging==21.3
pockets==0.9.1
Pygments==2.11.2
pyparsing==3.0.6
pytz==2021.3
requests==2.27.1
rstcheck==3.3.1
six==1.16.0
snowballstemmer==2.2.0
Sphinx==4.4.0
sphinx-rtd-theme==1.0.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-napoleon==0.7
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
urllib3==1.26.8
zipp==3.7.0

The error I was looking into has the same shape as the problem in issue rstcheck/rstcheck-core#66 . They may be related or this may be something different. This may also be an issue with docutils and if you determine I should open the issue there I'm happy to.

Cielquan commented 2 years ago

I tried to reproduce this issue but for me it works. Python 3.9.12 Linux Same dependencies.

Maybe try again with a new release. Maybe this helps somehow.

chrisgilmerproj commented 2 years ago

Ok, I tried this again. This time I used this as my way of reproducing. First, create a requirements.txt file with the contents listed above. Next create the file .rstcheck.cfg to look like:

[rstcheck]
ignore_messages=(Hyperlink target ".*" is not referenced\.$|Duplicate implicit target name: "(workers|images|solr)"\.$|Unexpected possible title overline or transition\.$)

After creating that file we'll do the rest inside a clean docker environment:

docker run -it --rm=true -v $PWD:$PWD -w $PWD python:3.9-slim bash

Inside of the docker container install the requirements and the latest version of rstcheck:

pip install -r requirements.txt
pip install rstcheck==5.0.0

Now run the command:

rstcheck -r ./my/docs/ --debug

I got this error

# rstcheck -r ./my/docs/ --debug
using config file /full/path/to/.rstcheck.cfg
multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/multiprocessing/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
  File "/usr/local/lib/python3.9/multiprocessing/pool.py", line 48, in mapstar
    return list(map(*args))
  File "/usr/local/lib/python3.9/site-packages/rstcheck/__init__.py", line 292, in _check_file
    for error in check(
  File "/usr/local/lib/python3.9/site-packages/rstcheck/__init__.py", line 204, in check
    docutils.core.publish_string(
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 407, in publish_string
    output, pub = publish_programmatically(
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 665, in publish_programmatically
    output = pub.publish(enable_exit_status=enable_exit_status)
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 217, in publish
    self.document = self.reader.read(self.source, self.parser,
  File "/usr/local/lib/python3.9/site-packages/docutils/readers/__init__.py", line 72, in read
    self.parse()
  File "/usr/local/lib/python3.9/site-packages/docutils/readers/__init__.py", line 78, in parse
    self.parser.parse(self.input, document)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/__init__.py", line 183, in parse
    self.statemachine.run(inputlines, document, inliner=self.inliner)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 170, in run
    results = StateMachineWS.run(self, input_lines, input_offset,
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 3008, in text
    self.section(title.lstrip(), source, style, lineno + 1, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
    newabsoffset = self.nested_parse(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
    state_machine.run(block, input_offset, memo=self.memo,
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 3008, in text
    self.section(title.lstrip(), source, style, lineno + 1, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
    newabsoffset = self.nested_parse(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
    state_machine.run(block, input_offset, memo=self.memo,
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2769, in underline
    self.section(title, source, style, lineno - 1, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
    newabsoffset = self.nested_parse(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
    state_machine.run(block, input_offset, memo=self.memo,
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2342, in explicit_markup
    nodelist, blank_finish = self.explicit_construct(match)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2354, in explicit_construct
    return method(self, expmatch)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2096, in directive
    return self.run_directive(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2146, in run_directive
    result = directive_instance.run()
  File "/usr/local/lib/python3.9/site-packages/sphinx/directives/patches.py", line 180, in run
    node['language'] = self.env.temp_data.get('highlight_language',
  File "/usr/local/lib/python3.9/site-packages/sphinx/util/docutils.py", line 350, in env
    return self.state.document.settings.env
AttributeError: 'Values' object has no attribute 'env'
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/rstcheck", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.9/site-packages/rstcheck/__init__.py", line 1040, in main
    results = pool.map(_check_file, [(name, args) for name in args.files])
  File "/usr/local/lib/python3.9/multiprocessing/pool.py", line 364, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "/usr/local/lib/python3.9/multiprocessing/pool.py", line 771, in get
    raise self._value
AttributeError: 'Values' object has no attribute 'env'

I can probably put together a version of the sphinx docs for you that would trigger this if it would be helpful. Thanks for your help with this.

Cielquan commented 2 years ago

I also tried it again with a clean venv and I still cannot reproduce.

Could you try to clone this repo and then try the procedure with this repo again and run rstcheck -r examples/good/ --debug. With this we can check if your docs may be the cause.

But it would also be good to have your rst source you try to lint. Maybe this will help.

chrisgilmerproj commented 2 years ago

I think you are correct that this has to do with my docs so I'll try to set up a minimal example. But for the record I used this procedure:

docker run -it --rm=true -v $PWD:$PWD -w $PWD python:3.9-slim bash

Inside the container:

apt-get update
apt-get install -y git gcc g++
git clone https://github.com/myint/rstcheck.git
cd rstcheck
pip install -e .
rstcheck -r examples/good/ --debug

This worked, telling me that there's something odd in my config that I need to account for. I'll try to get the minimal example together and send that to you.

chrisgilmerproj commented 2 years ago

I found the offending code. It looks like:

.. code::

    fields @timestamp, @message
    | filter @logStream like /imports|proxies/
    | filter message like /upload_id:11151/
    | sort @timestamp desc
    | limit 20

And it renders like:

Screen Shot 2022-04-18 at 9 58 38 AM

When I put that into a file by itself named test.rst and run rstcheck test.rst --debug I get the following:

Traceback (most recent call last):
  File "/usr/local/bin/rstcheck", line 8, in <module>
    sys.exit(main())
  File "/tmp/rstcheck/rstcheck/__init__.py", line 997, in main
    results = [_check_file((args.files[0], args))]
  File "/tmp/rstcheck/rstcheck/__init__.py", line 306, in _check_file
    for error in check(
  File "/tmp/rstcheck/rstcheck/__init__.py", line 217, in check
    docutils.core.publish_string(
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 407, in publish_string
    output, pub = publish_programmatically(
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 665, in publish_programmatically
    output = pub.publish(enable_exit_status=enable_exit_status)
  File "/usr/local/lib/python3.9/site-packages/docutils/core.py", line 217, in publish
    self.document = self.reader.read(self.source, self.parser,
  File "/usr/local/lib/python3.9/site-packages/docutils/readers/__init__.py", line 72, in read
    self.parse()
  File "/usr/local/lib/python3.9/site-packages/docutils/readers/__init__.py", line 78, in parse
    self.parser.parse(self.input, document)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/__init__.py", line 183, in parse
    self.statemachine.run(inputlines, document, inliner=self.inliner)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 170, in run
    results = StateMachineWS.run(self, input_lines, input_offset,
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/usr/local/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2342, in explicit_markup
    nodelist, blank_finish = self.explicit_construct(match)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2354, in explicit_construct
    return method(self, expmatch)
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2096, in directive
    return self.run_directive(
  File "/usr/local/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2146, in run_directive
    result = directive_instance.run()
  File "/usr/local/lib/python3.9/site-packages/sphinx/directives/patches.py", line 180, in run
    node['language'] = self.env.temp_data.get('highlight_language',
  File "/usr/local/lib/python3.9/site-packages/sphinx/util/docutils.py", line 350, in env
    return self.state.document.settings.env
AttributeError: 'Values' object has no attribute 'env'
Cielquan commented 2 years ago

The good news is that I can reproduce the issue now.

The bad news is that that seems to be an issue known for 7 years and being figured out: https://github.com/rstcheck/rstcheck/blame/v5.0.0/rstcheck/__init__.py#L216-L221

What language is the code snippet above?

chrisgilmerproj commented 2 years ago

Well that's a bummer :)

It's a query syntax used in AWS CloudWatch Logs for Log-Insights that is similar to SQL.

Do you think there is a different kind of block I could use here that wouldn't trigger the exception?

Cielquan commented 2 years ago

Thanks for the information.

That's exactly what I thought too. I guess the error occurs because no language is set for the code block and sphinx tries by default to parse the code as python source code.

The solution here is to set the language of the code block to text:

.. code:: text

    fields @timestamp, @message
    | filter @logStream like /imports|proxies/
    | filter message like /upload_id:11151/
    | sort @timestamp desc
    | limit 20
chrisgilmerproj commented 2 years ago

That's fascinating! Ok, so a simple fix and I'm good to go. Thanks.

Is there a rule I could turn on that would warn me if I haven't set the language for a code block? I imagine that's a useful thing for me to enforce in my code.

Cielquan commented 2 years ago

Unfortunately there is no rule. Maybe in the future when rstcheck/rstcheck-core#8 is implemented there may be a way to write a custom rule.

I myself am fairly new to the code base and started as maintainer just a week ago. But the deeper I dig the more I see rstchecks limitations. Thats why I am currently working on checking what rstcheck can do and cannot do with rstcheck/rstcheck#97. By now I think most open issues here are upstream limitations because of docutils which is used to parse the rst source.

chrisgilmerproj commented 2 years ago

Well I super appreciate your efforts to help me here. Thank you so much and good luck with this code base :)

Cielquan commented 2 years ago

Reopening the issue, because there are more issues with this bug. See rstcheck/rstcheck#121

Cielquan commented 2 years ago

Wrap up of rstcheck/rstcheck#121

This issue only occurs when sphinx is involved.

.. code:: python

    print(

.. code::

    print(

This snippet should produce the following output:

$ rstcheck test.rst
test.rst:3: (ERROR/3) (python) unexpected EOF while parsing

But instead the error is not found.


The official docs say, that no language must be present: https://docutils.sourceforge.io/docs/ref/rst/directives.html#code

Cielquan commented 2 years ago

The result from the above wrap-up is the same, regardless of the code directive used: code || code-block || sourcecode.

BUT I could observe that the sourcecode directive has a bit of another flow in code and somehow end with the same result.

If a code directive has no language defined and sphinx is present, this line in sphinx throws an AttributeError: https://github.com/sphinx-doc/sphinx/blob/4c664ae0b873af91b030a8da253959c0727e1c7a/sphinx/directives/patches.py#L172, when rstcheck runs.

When you however build the rst source with sphinx you get normal output in HTMl for example: image

Cielquan commented 2 years ago

Started work on fix-issue-with-sphinx-and-code-blocks-without-language branch.

Cielquan commented 2 years ago

My approach did fix the initial issue, but raised other issues like no longer detecting too short underlining.

I am currently investigating if a rstcheck sphinx-builder would be a possible solution to this problem.

EDIT: Work on rstcheck sphinx-builder has stopped.

arwedus commented 2 years ago

I'm getting this error on a file on a file with no code-block, highlight or even "verbatim text" block (the one's where the previous paragraph ends with ::) in it.

I unfortunately cannot share that file with you (confidential design document), but wanted to raise awareness for this issue.

CRITICAL:rstcheck_core.checker:An `AttributeError` error occured. This is most propably due to a code block directive (code/code-block/sourcecode) without a specified language. This may result in a false negative for source: 'documentation/...rst'. See https://rstcheck-core.readthedocs.io/en/latest/faq/#code-blocks-without-language-sphinx for more information.
Success! No issues detected.
fizyk commented 1 year ago

I'm having this issue with files containing .. include:: path directive

https://github.com/ClearcodeHQ/matchbox/blob/main/docs/source/contributing.rst

Cielquan commented 1 year ago

Code block responsible: https://github.com/rstcheck/rstcheck-core/blob/v1.1.0/src/rstcheck_core/checker.py#L220-L244

If someone finds a solution PRs are welcome. I edited my comments above with links to what I worked on before.