Custom Sphinx directive gives a pickling error - python-sphinx

I am trying to register a custom Sphinx directive on my conf.py, like this:
diff --git a/docs/conf.py b/docs/conf.py
index fda031a72..2d829602d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
## -2,6 +2,10 ## import os
import sys
from configparser import RawConfigParser
+from docutils import nodes
+from docutils.parsers.rst import directives
+from docutils.parsers.rst.directives.admonitions import BaseAdmonition
+
import sphinx_rtd_theme
sys.path.insert(0, os.path.abspath('..'))
## -153,5 +157,18 ## linkcheck_ignore = [
]
+class commercial(nodes.Admonition, nodes.Element):
+ pass
+
+
+class Commercial(BaseAdmonition):
+
+ node_class = commercial
+
+
def setup(app):
app.add_css_file('css/sphinx_prompt_css.css')
+
+ app.add_node(commercial)
+
+ directives.register_directive('commercial', Commercial)
but my make html fails with this error:
Exception occurred:
File "/home/juanlu/Projects/RTD/readthedocs.org/.venv/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 501, in write_doctree
pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
_pickle.PicklingError: Can't pickle <class 'commercial'>: attribute lookup commercial on builtins failed
full traceback: https://pastebin.com/fgHdDpJK
Anybody knows what's going on here?

This isn't possible, see this issue, and this one, and this one, and these docs:
It is important to know that while you can extend Sphinx without leaving your conf.py, if you declare an inherited node right there, you’ll hit an unobvious PickleError. So if something goes wrong, please make sure that you put inherited nodes into a separate Python module.

Related

pylint not generating messages for C0326 whitepace violations

pylint is not producing the expected convention warning messages for whitespace non-conformances.
python 3.6.8
pylint 2.13.5
using this test script :
from __future__ import print_function
import os, sys
import logging
from .. import views
class DoSomething(SomeCommand) : # space before colon !
def __init__(self):
for i in range(1,11): # no space after comma !!
if self.number == i:
print("matched")
else:
print ('not matched') # space before paren !!!
def check_user(self):
if self.user: return True
else : return False # spaces before colon !
results from pylint check :
PS C:\Users\PycharmProjects\coding_standard\src> pylint test_script.py
************* Module test_script
test_script.py:18:0: C0304: Final newline missing (missing-final-newline)
test_script.py:1:0: C0114: Missing module docstring (missing-module-docstring)
test_script.py:3:0: C0410: Multiple imports on one line (os, sys) (multiple-imports)
test_script.py:5:0: E0402: Attempted relative import beyond top-level package (relative-beyond-top-level)
test_script.py:7:0: C0115: Missing class docstring (missing-class-docstring)
test_script.py:7:18: E0602: Undefined variable 'SomeCommand' (undefined-variable)
test_script.py:16:4: C0116: Missing function or method docstring (missing-function-docstring)
test_script.py:17:8: R1703: The if statement can be replaced with 'return bool(test)' (simplifiable-if-statement)
test_script.py:17:8: R1705: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it (no-else-return)
test_script.py:17:22: C0321: More than one statement on a single line (multiple-statements)
test_script.py:7:0: R0903: Too few public methods (1/2) (too-few-public-methods)
test_script.py:3:0: W0611: Unused import os (unused-import)
test_script.py:3:0: W0611: Unused import sys (unused-import)
test_script.py:4:0: W0611: Unused import logging (unused-import)
test_script.py:5:0: W0611: Unused import views (unused-import)
Based on the docs I would expect to see C0326 messages for the commented lines.
I am using this as the reference for the conditions to be identified by C0326:
http://pylint-messages.wikidot.com/messages:c0326
Any suggestions to T/S this ?
Based on comment from #DylanLee ...
If I did it is truly by accident. I am not (knowingly) using any kind of config file to disable messages. Based on your comment, I ued the command line option:
$pylint --list-msgs-enabled
, which produces a list ...
Enabled messages:
...
multiple-statements (C0321)
superfluous-parens (C0325)
mixed-line-endings (C0327)
unexpected-line-ending-format (C0328)
wrong-spelling-in-comment (C0401)
...
Disabled messages:
raw-checker-failed (I0001)
bad-inline-option (I0010)
locally-disabled (I0011)
file-ignored (I0013)
suppressed-message (I0020)
useless-suppression (I0021)
deprecated-pragma (I0022)
use-symbolic-message-instead (I0023)
So, it is not obviously being disable, but is not included in the enabled list !
Maybe some updates to pylint that is suppressing that specific message ?
bad-whitespace has been removed from pylint in 2.6. You should use an autoformatter like black and pre-commit to handle this automatically.
See : https://pylint.pycqa.org/en/latest/whatsnew/2.6.html?highlight=bad-whitespace#other-changes
bad-continuation and bad-whitespace have been removed. black or another formatter can help you with this better than Pylint

Why is the output file from Biopython not found?

I work with a Mac. I have been trying to make a multiple sequence alignment in Python using Muscle. This is the code I have been running:
from Bio.Align.Applications import MuscleCommandline
cline = MuscleCommandline(input="testunaligned.fasta", out="testunaligned.aln", clwstrict=True)
print(cline)
from Bio import AlignIO
align = AlignIO.read(open("testunaligned.aln"), "clustal")
print(align)
I keep getting the following error:
FileNotFoundError: [Errno 2] No such file or directory: 'testunaligned.aln'
Does anyone know how I could fix this? I am very new to Python and computer science in general, and I am totally at a loss. Thanks!
cline in your code is an instance of MuscleCommandline object that you initialized with all the parameters. After the initialization, this instance can run muscle, but it will only do that if you call it. That means you have to invoke cline()
When you simply print the cline object, it will return a string that corresponds to the command you can manually run on the command line to get the same result as when you invoke cline().
And here the working code:
from Bio.Align.Applications import MuscleCommandline
cline = MuscleCommandline(
input="testunaligned.fasta",
out="testunaligned.aln",
clwstrict=True
)
print(cline)
cline() # this is where mucle runs
from Bio import AlignIO
align = AlignIO.read(open("testunaligned.aln"), "clustal")
print(align)

Declare additional dependency to sphinx-build in an extension

TL,DR: From a Sphinx extension, how do I tell sphinx-build to treat an additional file as a dependency? In my immediate use case, this is the extension's source code, but the question could equally apply to some auxiliary file used by the extension.
I'm generating documentation with Sphinx using a custom extension. I'm using sphinx-build to build the documentation. For example, I use this command to generate the HTML (this is the command in the makefile generated by sphinx-quickstart):
sphinx-build -b html -d _build/doctrees . _build/html
Since my custom extension is maintained together with the source of the documentation, I want sphinx-build to treat it as a dependency of the generated HTML (and LaTeX, etc.). So whenever I change my extension's source code, I want sphinx-build to regenerate the output.
How do I tell sphinx-build to treat an additional file as a dependency? That is not mentioned in the toctree, since it isn't part of the source. Logically, this should be something I do from my extension's setup function.
Sample extension (my_extension.py):
from docutils import nodes
from docutils.parsers.rst import Directive
class Foo(Directive):
def run(self):
node = nodes.paragraph(text='Hello world\n')
return [node]
def setup(app):
app.add_directive('foo', Foo)
Sample source (index.rst):
.. toctree::
:maxdepth: 2
.. foo::
Sample conf.py (basically the output of sphinx-quickstart plus my extension):
import sys
import os
sys.path.insert(0, os.path.abspath('.'))
extensions = ['my_extension']
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = 'Hello directive'
copyright = '2019, Gilles'
author = 'Gilles'
version = '1'
release = '1'
language = None
exclude_patterns = ['_build']
pygments_style = 'sphinx'
todo_include_todos = False
html_theme = 'alabaster'
html_static_path = ['_static']
htmlhelp_basename = 'Hellodirectivedoc'
latex_elements = {
}
latex_documents = [
(master_doc, 'Hellodirective.tex', 'Hello directive Documentation',
'Gilles', 'manual'),
]
man_pages = [
(master_doc, 'hellodirective', 'Hello directive Documentation',
[author], 1)
]
texinfo_documents = [
(master_doc, 'Hellodirective', 'Hello directive Documentation',
author, 'Hellodirective', 'One line description of project.',
'Miscellaneous'),
]
Validation of a solution:
Run make html (or sphinx-build as above).
Modify my_extension.py to replace Hello world by Hello again.
Run make html again.
The generated HTML (_build/html/index.html) must now contain Hello again instead of Hello world.
It looks like the note_dependency method in the build environment API should do what I want. But when should I call it? I tried various events but none seemed to hit the environment object in the right state. What did work was to call it from a directive.
import os
from docutils import nodes
from docutils.parsers.rst import Directive
import sphinx.application
class Foo(Directive):
def run(self):
self.state.document.settings.env.note_dependency(__file__)
node = nodes.paragraph(text='Hello done\n')
return [node]
def setup(app):
app.add_directive('foo', Foo)
If a document contains at least one foo directive, it'll get marked as stale when the extension that introduces this directive changes. This makes sense, although it could get tedious if an extension adds many directives or makes different changes. I don't know if there's a better way.
Inspired by Luc Van Oostenryck's autodoc-C.
As far as I know app.env.note_dependency can be called within the doctree-read to add any file as a dependency to the document currently being read.
So in your use case, I assume this would work:
from typing import Any, Dict
from sphinx.application import Sphinx
import docutils.nodes as nodes
def doctree-read(app: Sphinx, doctree: nodes.document):
app.env.note_dependency(file)
def setup(app: Sphinx):
app.connect("doctree-read", doctree-read)

How to include pygments styles in a sphinx project?

Sphinx can define themes as well as a pygments style to use.
I couldn't however - find a good way for a Sphinx project to define a custom style (color scheme) for pygments to use.
From the docs:
To make the style usable for Pygments, you must
either register it as a plugin (see the plugin docs)
or drop it into the styles subpackage of your Pygments distribution one style class per style, where the file name is the style name and the class name is StylenameClass.
From what I can tell the first option is what I'm after since it should be possible to extend pygments dynamically. Although from checking the link I'm not sure how this would be done (no examples of how to use the plugin system).
The second example involves copying files into pygments which isn't practical especially since the path may not be writable by the user.
I did manage to hack in a style, although it's not a nice solution:
including for completeness
# Sphinx "conf.py"
# Master toctree document
master_doc = 'contents'
# BEGIN MONKEY-PATCH
from pygments.style import Style
from pygments.token import Text, Other, Comment, Whitespace
class MyFancyStyle(Style):
background_color = "#1e1e27"
default_style = ""
styles = {
Text: "#cfbfad",
Other: "#cfbfad",
Whitespace: "#434357",
Comment: "#cd8b00",
Comment.Preproc: "#409090",
Comment.PreprocFile: "bg:#404040 #ffcd8b",
Comment.Special: "#808bed",
# ... snip (just more colors, you get the idea) ...
}
def pygments_monkeypatch_style(mod_name, cls):
import sys
import pygments.styles
cls_name = cls.__name__
mod = type(__import__("os"))(mod_name)
setattr(mod, cls_name, cls)
setattr(pygments.styles, mod_name, mod)
sys.modules["pygments.styles." + mod_name] = mod
from pygments.styles import STYLE_MAP
STYLE_MAP[mod_name] = mod_name + "::" + cls_name
pygments_monkeypatch_style("my_fancy_style", MyFancyStyle)
pygments_style = "my_fancy_style"
# END MONKEY-PATCH
In your conf.py specify the Pygments style you want to use. From the Sphinx documentation:
pygments_style
The style name to use for Pygments highlighting of source code. If not set, either the theme’s default style or 'sphinx' is selected for HTML output.
Available names can be retrieved by:
>>> from pygments.styles import get_all_styles
>>> styles = list(get_all_styles())
An online preview of some Sphinx theme and Pygments style combinations is available.
If out-of-the-box Pygments styles are not to your liking, then you can create a custom Pygments style.
This is how I set it up:
Folder Structure:
docs
├── source
│ ├── conf.py <--
│ ├── _pygments
│ │ ├── style.py <--
│ ├── _static
│ ├── ...
conf.py
(at the very top)
import sys, os
sys.path.append(os.path.abspath("./_pygments"))
pygments_style = 'style.MonokaiStyle'
...
style.py
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, Text, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class MonokaiStyle(Style):
"""
This style mimics the Monokai color scheme.
"""
background_color = "#272822"
highlight_color = "#49483e"
styles = {
# No corresponding class for the following:
Text: "#f8f8f2", # class: ''
Whitespace: "", # class: 'w'
Error: "#960050 bg:#1e0010", # class: 'err'
Other: "", # class 'x'
...
}
You can choose a predefined style here (as I did) and put the corresponding *.py file from the official pygment repo into the _pygment folder. Or you can define your own style, renaming the class to your liking (don't forget to adapt the import clause in conf.py to the new names)
EDIT: This is an even better resource for style lookup.
I had a similar need, though what I really wanted was to slightly change an existing style (called the base style below). I was able to extent the code in the question for my needs. I post it here for anyone else coming across this problem.
# Sphinx "conf.py"
# Syntax highlighting of code blocks
import pygments.styles, pygments.token
def monkeypatch_pygments(name, base_name='default', attrs={}):
import importlib, sys
base_module = importlib.import_module('.'.join(['pygments', 'styles', base_name]))
def name_to_class_name(name):
return name.capitalize() + 'Style'
base_class = getattr(base_module, name_to_class_name(base_name))
styles = getattr(base_class, 'styles', {}).copy()
styles.update(attrs.pop('styles', {}))
attrs['styles'] = styles
class_name = name_to_class_name(name)
Style = type(class_name, (base_class,), attrs)
module = type(base_module)(name)
setattr(module, class_name, Style)
setattr(pygments.styles, name, module)
pygments.styles.STYLE_MAP[name] = f'{name}::{class_name}'
sys.modules['.'.join(['pygments', 'styles', name])] = module
pygments_style = 'custom' # Arbitrary name of new style
monkeypatch_pygments(
pygments_style,
'friendly', # Name of base style to use
{
# Changes to base style
'background_color': '#f6f6f6',
'styles': {
pygments.token.Comment: 'italic #688F98',
pygments.token.Name.Variable: '#d27a0a',
},
},
)
In the above example, the friendly style is used as the base style. Its 'background_color' and a few items within the 'styles' dictionary are redefined. Note that items not specified in 'styles' will be taken from the base style. The base style itself is not changed.
The "proper" way to include a pygments style as a plugin is to make a new package and install it via setuptools.
Use the usual process to create a setup.cfg (https://setuptools.readthedocs.io/en/latest/userguide/declarative_config.html)
Add the following section to it:
[options.entry_points]
pygments.styles =
my_fancy_style = mypackage.mystyle:MyFancyStyle
Create your style as class MyFancyStyle in mypackage/mystyle.py
Install the package: pip install -e .
Profit! You can now refer use "my_fancy_style" anywhere a Pygments style is expected. Phew.
I adopt the solution of Glades and it works well, except in the case that the style-python package is installed (style-1.1.0 in my case).
It lead to the ERROR (sorry for the part of french in the ouput) :
Une exception a été levée :
File "/home/bp/.local/lib/python3.8/site-packages/style/styled_string_builder.py", line 44, in __getattr__
raise AttributeError('%r object has no attribute %r' % (self.__class__.__name__, attr))
AttributeError: '_StyledStringBuilder' object has no attribute 'MonokaiStyle'
Only solution I find is just to uninstall this package named style :
$ pip uninstall style

Python error: one of the arguments is required

I'm trying to run a code from github that uses Python to classify images but I'm getting an error.
here is the code:
import argparse as ap
import cv2
import imutils
import numpy as np
import os
from sklearn.svm import LinearSVC
from sklearn.externals import joblib
from scipy.cluster.vq import *
# Get the path of the testing set
parser = ap.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-t", "--testingSet", help="Path to testing Set")
group.add_argument("-i", "--image", help="Path to image")
parser.add_argument('-v',"--visualize", action='store_true')
args = vars(parser.parse_args())
# Get the path of the testing image(s) and store them in a list
image_paths = []
if args["testingSet"]:
test_path = args["testingSet"]
try:
testing_names = os.listdir(test_path)
except OSError:
print "No such directory {}\nCheck if the file exists".format(test_path)
exit()
for testing_name in testing_names:
dir = os.path.join(test_path, testing_name)
class_path = imutils.imlist(dir)
image_paths+=class_path
else:
image_paths = [args["image"]]
and this is the error message I'm getting
usage: getClass.py [-h]
(- C:/Users/Lenovo/Downloads/iris/bag-of-words-master/dataset/test TESTINGSET | - C:/Users/Lenovo/Downloads/iris/bag-of-words-master/dataset/test/test_1.jpg IMAGE)
[- C:/Users/Lenovo/Downloads/iris/bag-of-words-master/dataset]
getClass.py: error: one of the arguments - C:/Users/Lenovo/Downloads/iris/bag-of-words-master/dataset/test/--testingSet - C:/Users/Lenovo/Downloads/iris/bag-of-words-master/dataset/test/test_1.jpg/--image is required
can you please help me with this? where and how should I write the file path?
This is an error your own program is issuing. The message is not about the file path but about the number of arguments. This line
group = parser.add_mutually_exclusive_group(required=True)
says that only one of your command-line arguments (-t, -i) is permitted. But it appears from the error message that you are supplying both --testingSet and --image on your command line.
Since you only have 3 arguments, I have to wonder if you really need argument groups at all.
To get your command line to work, drop the mutually-exclusive group and add the arguments to the parser directly.
parser.add_argument("-t", "--testingSet", help="Path to testing Set")
parser.add_argument("-i", "--image", help="Path to image")
parser.add_argument('-v',"--visualize", action='store_true')

Resources