diff --git a/broken_examples/complex_report.py b/broken_examples/complex_report.py index fa84c85e..752cc223 100644 --- a/broken_examples/complex_report.py +++ b/broken_examples/complex_report.py @@ -12,10 +12,25 @@ # begin-doc-include import os -from pylatex import Document, PageStyle, Head, Foot, MiniPage, \ - StandAloneGraphic, MultiColumn, Tabu, LongTabu, LargeText, MediumText, \ - LineBreak, NewPage, Tabularx, TextColor, simple_page_number -from pylatex.utils import bold, NoEscape +from pylatex import ( + Document, + Foot, + Head, + LargeText, + LineBreak, + LongTabu, + MediumText, + MiniPage, + MultiColumn, + NewPage, + PageStyle, + StandAloneGraphic, + Tabu, + Tabularx, + TextColor, + simple_page_number, +) +from pylatex.utils import NoEscape, bold def generate_unique(): @@ -23,7 +38,7 @@ def generate_unique(): "head": "40pt", "margin": "0.5in", "bottom": "0.6in", - "includeheadfoot": True + "includeheadfoot": True, } doc = Document(geometry_options=geometry_options) @@ -32,17 +47,19 @@ def generate_unique(): # Header image with first_page.create(Head("L")) as header_left: - with header_left.create(MiniPage(width=NoEscape(r"0.49\textwidth"), - pos='c')) as logo_wrapper: - logo_file = os.path.join(os.path.dirname(__file__), - 'sample-logo.png') - logo_wrapper.append(StandAloneGraphic(image_options="width=120px", - filename=logo_file)) + with header_left.create( + MiniPage(width=NoEscape(r"0.49\textwidth"), pos="c") + ) as logo_wrapper: + logo_file = os.path.join(os.path.dirname(__file__), "sample-logo.png") + logo_wrapper.append( + StandAloneGraphic(image_options="width=120px", filename=logo_file) + ) # Add document title with first_page.create(Head("R")) as right_header: - with right_header.create(MiniPage(width=NoEscape(r"0.49\textwidth"), - pos='c', align='r')) as title_wrapper: + with right_header.create( + MiniPage(width=NoEscape(r"0.49\textwidth"), pos="c", align="r") + ) as title_wrapper: title_wrapper.append(LargeText(bold("Bank Account Statement"))) title_wrapper.append(LineBreak()) title_wrapper.append(MediumText(bold("Date"))) @@ -50,37 +67,37 @@ def generate_unique(): # Add footer with first_page.create(Foot("C")) as footer: message = "Important message please read" - with footer.create(Tabularx( - "X X X X", - width_argument=NoEscape(r"\textwidth"))) as footer_table: - + with footer.create( + Tabularx("X X X X", width_argument=NoEscape(r"\textwidth")) + ) as footer_table: footer_table.add_row( - [MultiColumn(4, align='l', data=TextColor("blue", message))]) + [MultiColumn(4, align="l", data=TextColor("blue", message))] + ) footer_table.add_hline(color="blue") footer_table.add_empty_row() - branch_address = MiniPage( - width=NoEscape(r"0.25\textwidth"), - pos='t') + branch_address = MiniPage(width=NoEscape(r"0.25\textwidth"), pos="t") branch_address.append("960 - 22nd street east") branch_address.append("\n") branch_address.append("Saskatoon, SK") - document_details = MiniPage(width=NoEscape(r"0.25\textwidth"), - pos='t', align='r') + document_details = MiniPage( + width=NoEscape(r"0.25\textwidth"), pos="t", align="r" + ) document_details.append("1000") document_details.append(LineBreak()) document_details.append(simple_page_number()) - footer_table.add_row([branch_address, branch_address, - branch_address, document_details]) + footer_table.add_row( + [branch_address, branch_address, branch_address, document_details] + ) doc.preamble.append(first_page) # End first page style # Add customer information with doc.create(Tabu("X[l] X[r]")) as first_page_table: - customer = MiniPage(width=NoEscape(r"0.49\textwidth"), pos='h') + customer = MiniPage(width=NoEscape(r"0.49\textwidth"), pos="h") customer.append("Verna Volcano") customer.append("\n") customer.append("For some Person") @@ -92,8 +109,7 @@ def generate_unique(): customer.append("Address3") # Add branch information - branch = MiniPage(width=NoEscape(r"0.49\textwidth"), pos='t!', - align='r') + branch = MiniPage(width=NoEscape(r"0.49\textwidth"), pos="t!", align="r") branch.append("Branch no.") branch.append(LineBreak()) branch.append(bold("1181...")) @@ -107,15 +123,14 @@ def generate_unique(): doc.add_color(name="lightgray", model="gray", description="0.80") # Add statement table - with doc.create(LongTabu("X[l] X[2l] X[r] X[r] X[r]", - row_height=1.5)) as data_table: - data_table.add_row(["date", - "description", - "debits($)", - "credits($)", - "balance($)"], - mapper=bold, - color="lightgray") + with doc.create( + LongTabu("X[l] X[2l] X[r] X[r] X[r]", row_height=1.5) + ) as data_table: + data_table.add_row( + ["date", "description", "debits($)", "credits($)", "balance($)"], + mapper=bold, + color="lightgray", + ) data_table.add_empty_row() data_table.add_hline() row = ["2016-JUN-01", "Test", "$100", "$1000", "-$900"] @@ -129,12 +144,12 @@ def generate_unique(): # Add cheque images with doc.create(LongTabu("X[c] X[c]")) as cheque_table: - cheque_file = os.path.join(os.path.dirname(__file__), - 'chequeexample.png') + cheque_file = os.path.join(os.path.dirname(__file__), "chequeexample.png") cheque = StandAloneGraphic(cheque_file, image_options="width=200px") for i in range(0, 20): cheque_table.add_row([cheque, cheque]) doc.generate_pdf("complex_report", clean_tex=False) + generate_unique() diff --git a/broken_examples/longtabu.py b/broken_examples/longtabu.py index 24028869..58c3dbae 100644 --- a/broken_examples/longtabu.py +++ b/broken_examples/longtabu.py @@ -9,7 +9,7 @@ """ # begin-doc-include -from pylatex import Document, LongTabu, HFill +from pylatex import Document, HFill, LongTabu from pylatex.utils import bold @@ -19,7 +19,7 @@ def genenerate_longtabu(): "margin": "0.5in", "headheight": "20pt", "headsep": "10pt", - "includeheadfoot": True + "includeheadfoot": True, } doc = Document(page_numbers=True, geometry_options=geometry_options) @@ -30,8 +30,7 @@ def genenerate_longtabu(): data_table.add_hline() data_table.add_empty_row() data_table.end_table_header() - data_table.add_row(["Prov", "Num", "CurBal", "IntPay", "Total", - "IntR"]) + data_table.add_row(["Prov", "Num", "CurBal", "IntPay", "Total", "IntR"]) row = ["PA", "9", "$100", "%10", "$1000", "Test"] for i in range(50): data_table.add_row(row) @@ -42,4 +41,5 @@ def genenerate_longtabu(): doc.generate_pdf("longtabu", clean_tex=False) + genenerate_longtabu() diff --git a/broken_examples/tabus.py b/broken_examples/tabus.py index 0bbcd204..1125312b 100644 --- a/broken_examples/tabus.py +++ b/broken_examples/tabus.py @@ -8,7 +8,8 @@ # begin-doc-include from random import randint -from pylatex import Document, LongTabu, Tabu, Center + +from pylatex import Center, Document, LongTabu, Tabu from pylatex.utils import bold @@ -18,7 +19,7 @@ def genenerate_tabus(): "margin": "1.5in", "headheight": "20pt", "headsep": "10pt", - "includeheadfoot": True + "includeheadfoot": True, } doc = Document(page_numbers=True, geometry_options=geometry_options) @@ -30,8 +31,7 @@ def genenerate_tabus(): data_table.add_hline() data_table.add_empty_row() data_table.end_table_header() - data_table.add_row(["Prov", "Num", "CurBal", "IntPay", "Total", - "IntR"]) + data_table.add_row(["Prov", "Num", "CurBal", "IntPay", "Total", "IntR"]) row = ["PA", "9", "$100", "%10", "$1000", "Test"] for i in range(40): data_table.add_row(row) @@ -56,4 +56,5 @@ def genenerate_tabus(): doc.generate_pdf("tabus", clean_tex=False) + genenerate_tabus() diff --git a/docs/gen_example_title.py b/docs/gen_example_title.py index a268f475..23e33a46 100644 --- a/docs/gen_example_title.py +++ b/docs/gen_example_title.py @@ -2,11 +2,11 @@ title = sys.argv[1] -if title.endswith('_ex'): +if title.endswith("_ex"): title = title[:-3] -title = title.replace('_', ' ') -title = title.capitalize() + ' example' +title = title.replace("_", " ") +title = title.capitalize() + " example" print(title) -print(len(title) * '=') +print(len(title) * "=") diff --git a/docs/source/conf.py b/docs/source/conf.py index be410172..f26eec9f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,22 +13,22 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys - # Needed for old sphinx version to work import collections +import sys + if sys.version_info >= (3, 10): collections.Callable = collections.abc.Callable -import os import inspect -import sphinx_rtd_theme +import os +import sphinx_rtd_theme # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../../')) +sys.path.insert(0, os.path.abspath("../../")) from pylatex import __version__ # -- General configuration ------------------------------------------------ @@ -40,17 +40,17 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', - 'sphinx.ext.ifconfig', - 'sphinx.ext.intersphinx', - 'sphinx.ext.autosummary', - 'sphinx.ext.extlinks', - 'sphinx.ext.napoleon', - 'sphinx.ext.linkcode', + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.mathjax", + "sphinx.ext.ifconfig", + "sphinx.ext.intersphinx", + "sphinx.ext.autosummary", + "sphinx.ext.extlinks", + "sphinx.ext.napoleon", + "sphinx.ext.linkcode", ] napoleon_include_special_with_doc = False @@ -58,30 +58,30 @@ numpydoc_class_members_toctree = False # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'PyLaTeX' -copyright = '2015, Jelte Fennema' -author = 'Jelte Fennema' +project = "PyLaTeX" +copyright = "2015, Jelte Fennema" +author = "Jelte Fennema" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = __version__.rstrip('.dirty') +version = __version__.rstrip(".dirty") # The full version, including alpha/beta/rc tags. release = version @@ -98,9 +98,9 @@ # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' -autodoc_member_order = 'bysource' -autodoc_default_flags = ['inherited-members'] -autoclass_content = 'both' +autodoc_member_order = "bysource" +autodoc_default_flags = ["inherited-members"] +autoclass_content = "both" def auto_change_docstring(app, what, name, obj, options, lines): @@ -111,54 +111,70 @@ def auto_change_docstring(app, what, name, obj, options, lines): - Add a title to module docstrings - Merge lines that end with a '\' with the next line. """ - if what == 'module' and name.startswith('pylatex'): - lines.insert(0, len(name) * '=') + if what == "module" and name.startswith("pylatex"): + lines.insert(0, len(name) * "=") lines.insert(0, name) hits = 0 for i, line in enumerate(lines.copy()): - if line.endswith('\\'): + if line.endswith("\\"): lines[i - hits] += lines.pop(i + 1 - hits) hits += 1 -def autodoc_allow_most_inheritance(app, what, name, obj, namespace, skip, - options): - cls = namespace.split('.')[-1] +def autodoc_allow_most_inheritance(app, what, name, obj, namespace, skip, options): + cls = namespace.split(".")[-1] members = { - 'object': ['dump', 'dumps_packages', 'dump_packages', 'latex_name', - 'escape', 'generate_tex', 'packages', 'dumps_as_content', - 'end_paragraph', 'separate_paragraph', 'content_separator'], - - 'container': ['create', 'dumps', 'dumps_content', 'begin_paragraph'], - - 'userlist': ['append', 'clear', 'copy', 'count', 'extend', 'index', - 'insert', 'pop', 'remove', 'reverse', 'sort'], - 'error': ['args', 'with_traceback'], + "object": [ + "dump", + "dumps_packages", + "dump_packages", + "latex_name", + "escape", + "generate_tex", + "packages", + "dumps_as_content", + "end_paragraph", + "separate_paragraph", + "content_separator", + ], + "container": ["create", "dumps", "dumps_content", "begin_paragraph"], + "userlist": [ + "append", + "clear", + "copy", + "count", + "extend", + "index", + "insert", + "pop", + "remove", + "reverse", + "sort", + ], + "error": ["args", "with_traceback"], } - members['all'] = list(set([req for reqs in members.values() for req in - reqs])) + members["all"] = list(set([req for reqs in members.values() for req in reqs])) - if name in members['all']: + if name in members["all"]: skip = True - if cls == 'LatexObject': + if cls == "LatexObject": return False - if cls in ('Container', 'Environment') and \ - name in members['container']: + if cls in ("Container", "Environment") and name in members["container"]: return False - if cls == 'Document' and name == 'generate_tex': + if cls == "Document" and name == "generate_tex": return False - if name == 'separate_paragraph' and cls in ('SubFigure', 'Float'): + if name == "separate_paragraph" and cls in ("SubFigure", "Float"): return False # Ignore all functions of NoEscape, since it is inherited - if cls == 'NoEscape': + if cls == "NoEscape": return True return skip @@ -166,30 +182,30 @@ def autodoc_allow_most_inheritance(app, what, name, obj, namespace, skip, def setup(app): """Connect autodoc event to custom handler.""" - app.connect('autodoc-process-docstring', auto_change_docstring) - app.connect('autodoc-skip-member', autodoc_allow_most_inheritance) + app.connect("autodoc-process-docstring", auto_change_docstring) + app.connect("autodoc-skip-member", autodoc_allow_most_inheritance) def linkcode_resolve(domain, info): """A simple function to find matching source code.""" - module_name = info['module'] - fullname = info['fullname'] - attribute_name = fullname.split('.')[-1] - base_url = 'https://github.com/JelteF/PyLaTeX/' - - if '+' in version: - commit_hash = version.split('.')[-1][1:] - base_url += 'tree/%s/' % commit_hash + module_name = info["module"] + fullname = info["fullname"] + attribute_name = fullname.split(".")[-1] + base_url = "https://github.com/JelteF/PyLaTeX/" + + if "+" in version: + commit_hash = version.split(".")[-1][1:] + base_url += "tree/%s/" % commit_hash else: - base_url += 'blob/v%s/' % version + base_url += "blob/v%s/" % version - filename = module_name.replace('.', '/') + '.py' + filename = module_name.replace(".", "/") + ".py" module = sys.modules.get(module_name) # Get the actual object try: actual_object = module - for obj in fullname.split('.'): + for obj in fullname.split("."): parent = actual_object actual_object = getattr(actual_object, obj) except AttributeError: @@ -217,7 +233,7 @@ def linkcode_resolve(domain, info): else: end_line = start_line + len(source) - 1 - line_anchor = '#L%d-L%d' % (start_line, end_line) + line_anchor = "#L%d-L%d" % (start_line, end_line) return base_url + filename + line_anchor @@ -228,7 +244,7 @@ def linkcode_resolve(domain, info): # The reST default role (used for this markup: `text`) to use for all # documents. -default_role = 'py:obj' +default_role = "py:obj" # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True @@ -242,10 +258,10 @@ def linkcode_resolve(domain, info): # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['pylatex.'] +modindex_common_prefix = ["pylatex."] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False @@ -254,11 +270,10 @@ def linkcode_resolve(domain, info): todo_include_todos = True intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), - 'matplotlib': ('http://matplotlib.org/', None), - 'numpy': ('https://docs.scipy.org/doc/numpy/', None), - 'quantities': ('https://pythonhosted.org/quantities/', - 'quantities-inv.txt'), + "python": ("https://docs.python.org/3", None), + "matplotlib": ("http://matplotlib.org/", None), + "numpy": ("https://docs.scipy.org/doc/numpy/", None), + "quantities": ("https://pythonhosted.org/quantities/", "quantities-inv.txt"), } @@ -266,7 +281,7 @@ def linkcode_resolve(domain, info): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -291,12 +306,12 @@ def linkcode_resolve(domain, info): # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = '_static/realfavicongenerator.ico' +html_favicon = "_static/realfavicongenerator.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -359,20 +374,17 @@ def linkcode_resolve(domain, info): # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'PyLaTeXdoc' +htmlhelp_basename = "PyLaTeXdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # 'preamble': '', - # Latex figure (float) alignment # 'figure_align': 'htbp', } @@ -381,8 +393,7 @@ def linkcode_resolve(domain, info): # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'PyLaTeX.tex', 'PyLaTeX Documentation', - 'Jelte Fennema', 'manual'), + (master_doc, "PyLaTeX.tex", "PyLaTeX Documentation", "Jelte Fennema", "manual"), ] # The name of an image file (relative to this directory) to place at the top of @@ -410,10 +421,7 @@ def linkcode_resolve(domain, info): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'pylatex', 'PyLaTeX Documentation', - [author], 1) -] +man_pages = [(master_doc, "pylatex", "PyLaTeX Documentation", [author], 1)] # If true, show URL addresses after external links. # man_show_urls = False @@ -425,8 +433,15 @@ def linkcode_resolve(domain, info): # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'PyLaTeX', 'PyLaTeX Documentation', author, 'PyLaTeX', - 'One line description of project.', 'Miscellaneous'), + ( + master_doc, + "PyLaTeX", + "PyLaTeX Documentation", + author, + "PyLaTeX", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. diff --git a/examples/basic.py b/examples/basic.py index ff66ae56..69f4f80b 100644 --- a/examples/basic.py +++ b/examples/basic.py @@ -7,8 +7,8 @@ """ # begin-doc-include -from pylatex import Document, Section, Subsection, Command -from pylatex.utils import italic, NoEscape +from pylatex import Command, Document, Section, Subsection +from pylatex.utils import NoEscape, italic def fill_document(doc): @@ -17,17 +17,17 @@ def fill_document(doc): :param doc: the document :type doc: :class:`pylatex.document.Document` instance """ - with doc.create(Section('A section')): - doc.append('Some regular text and some ') - doc.append(italic('italic text. ')) + with doc.create(Section("A section")): + doc.append("Some regular text and some ") + doc.append(italic("italic text. ")) - with doc.create(Subsection('A subsection')): - doc.append('Also some crazy characters: $&#{}') + with doc.create(Subsection("A subsection")): + doc.append("Also some crazy characters: $&#{}") -if __name__ == '__main__': +if __name__ == "__main__": # Basic document - doc = Document('basic') + doc = Document("basic") fill_document(doc) doc.generate_pdf(clean_tex=False) @@ -36,18 +36,18 @@ def fill_document(doc): # Document with `\maketitle` command activated doc = Document() - doc.preamble.append(Command('title', 'Awesome Title')) - doc.preamble.append(Command('author', 'Anonymous author')) - doc.preamble.append(Command('date', NoEscape(r'\today'))) - doc.append(NoEscape(r'\maketitle')) + doc.preamble.append(Command("title", "Awesome Title")) + doc.preamble.append(Command("author", "Anonymous author")) + doc.preamble.append(Command("date", NoEscape(r"\today"))) + doc.append(NoEscape(r"\maketitle")) fill_document(doc) - doc.generate_pdf('basic_maketitle', clean_tex=False) + doc.generate_pdf("basic_maketitle", clean_tex=False) # Add stuff to the document - with doc.create(Section('A second section')): - doc.append('Some text.') + with doc.create(Section("A second section")): + doc.append("Some text.") - doc.generate_pdf('basic_maketitle2', clean_tex=False) + doc.generate_pdf("basic_maketitle2", clean_tex=False) tex = doc.dumps() # The document as string in LaTeX syntax diff --git a/examples/basic_inheritance.py b/examples/basic_inheritance.py index 6831310c..fade8199 100644 --- a/examples/basic_inheritance.py +++ b/examples/basic_inheritance.py @@ -7,31 +7,30 @@ """ # begin-doc-include -from pylatex import Document, Section, Subsection, Command -from pylatex.utils import italic, NoEscape +from pylatex import Command, Document, Section, Subsection +from pylatex.utils import NoEscape, italic class MyDocument(Document): def __init__(self): super().__init__() - self.preamble.append(Command('title', 'Awesome Title')) - self.preamble.append(Command('author', 'Anonymous author')) - self.preamble.append(Command('date', NoEscape(r'\today'))) - self.append(NoEscape(r'\maketitle')) + self.preamble.append(Command("title", "Awesome Title")) + self.preamble.append(Command("author", "Anonymous author")) + self.preamble.append(Command("date", NoEscape(r"\today"))) + self.append(NoEscape(r"\maketitle")) def fill_document(self): """Add a section, a subsection and some text to the document.""" - with self.create(Section('A section')): - self.append('Some regular text and some ') - self.append(italic('italic text. ')) + with self.create(Section("A section")): + self.append("Some regular text and some ") + self.append(italic("italic text. ")) - with self.create(Subsection('A subsection')): - self.append('Also some crazy characters: $&#{}') + with self.create(Subsection("A subsection")): + self.append("Also some crazy characters: $&#{}") -if __name__ == '__main__': - +if __name__ == "__main__": # Document doc = MyDocument() @@ -39,8 +38,8 @@ def fill_document(self): doc.fill_document() # Add stuff to the document - with doc.create(Section('A second section')): - doc.append('Some text.') + with doc.create(Section("A second section")): + doc.append("Some text.") - doc.generate_pdf('basic_inheritance', clean_tex=False) + doc.generate_pdf("basic_inheritance", clean_tex=False) tex = doc.dumps() # The document as string in LaTeX syntax diff --git a/examples/config.py b/examples/config.py index 8de20276..86d20d2a 100644 --- a/examples/config.py +++ b/examples/config.py @@ -7,10 +7,10 @@ """ # begin-doc-include -from pylatex import Document, NoEscape import pylatex.config as cf +from pylatex import Document, NoEscape -lorem = ''' +lorem = """ Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus facilisis tortor vel imperdiet vestibulum. Vivamus et mollis risus. Proin ut enim eu leo volutpat tristique. Vivamus quam enim, @@ -33,25 +33,25 @@ orci ut sodales ullamcorper. Integer bibendum elementum convallis. Praesent accumsan at leo eget ullamcorper. Maecenas eget tempor enim. Quisque et nisl eros. -''' +""" def main(): cf.active = cf.Version1() doc = Document(data=NoEscape(lorem)) - doc.generate_pdf('config1_with_indent', clean_tex=False) + doc.generate_pdf("config1_with_indent", clean_tex=False) cf.active = cf.Version1(indent=False) doc = Document(data=NoEscape(lorem)) - doc.generate_pdf('config2_without_indent', clean_tex=False) + doc.generate_pdf("config2_without_indent", clean_tex=False) with cf.Version1().use(): doc = Document(data=NoEscape(lorem)) - doc.generate_pdf('config3_with_indent_again', clean_tex=False) + doc.generate_pdf("config3_with_indent_again", clean_tex=False) doc = Document(data=NoEscape(lorem)) - doc.generate_pdf('config4_without_indent_again', clean_tex=False) + doc.generate_pdf("config4_without_indent_again", clean_tex=False) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/environment_ex.py b/examples/environment_ex.py index 4061c263..d26bb45c 100644 --- a/examples/environment_ex.py +++ b/examples/environment_ex.py @@ -7,41 +7,47 @@ """ # begin-doc-include +from pylatex import Document, Section from pylatex.base_classes import Environment from pylatex.package import Package -from pylatex import Document, Section from pylatex.utils import NoEscape class AllTT(Environment): """A class to wrap LaTeX's alltt environment.""" - packages = [Package('alltt')] + packages = [Package("alltt")] escape = False content_separator = "\n" + # Create a new document doc = Document() -with doc.create(Section('Wrapping Latex Environments')): - doc.append(NoEscape( - r""" +with doc.create(Section("Wrapping Latex Environments")): + doc.append( + NoEscape( + r""" The following is a demonstration of a custom \LaTeX{} command with a couple of parameters. - """)) + """ + ) + ) # Put some data inside the AllTT environment with doc.create(AllTT()): - verbatim = ("This is verbatim, alltt, text.\n\n\n" - "Setting \\underline{escape} to \\underline{False} " - "ensures that text in the environment is not\n" - "subject to escaping...\n\n\n" - "Setting \\underline{content_separator} " - "ensures that line endings are broken in\n" - "the latex just as they are in the input text.\n" - "alltt supports math: \\(x^2=10\\)") + verbatim = ( + "This is verbatim, alltt, text.\n\n\n" + "Setting \\underline{escape} to \\underline{False} " + "ensures that text in the environment is not\n" + "subject to escaping...\n\n\n" + "Setting \\underline{content_separator} " + "ensures that line endings are broken in\n" + "the latex just as they are in the input text.\n" + "alltt supports math: \\(x^2=10\\)" + ) doc.append(verbatim) doc.append("This is back to normal text...") # Generate pdf -doc.generate_pdf('environment_ex', clean_tex=False) +doc.generate_pdf("environment_ex", clean_tex=False) diff --git a/examples/full.py b/examples/full.py index 67ce48cb..06f97264 100755 --- a/examples/full.py +++ b/examples/full.py @@ -10,28 +10,40 @@ """ # begin-doc-include +import os + import numpy as np -from pylatex import Document, Section, Subsection, Tabular, Math, TikZ, Axis, \ - Plot, Figure, Matrix, Alignat +from pylatex import ( + Alignat, + Axis, + Document, + Figure, + Math, + Matrix, + Plot, + Section, + Subsection, + Tabular, + TikZ, +) from pylatex.utils import italic -import os -if __name__ == '__main__': - image_filename = os.path.join(os.path.dirname(__file__), 'kitten.jpg') +if __name__ == "__main__": + image_filename = os.path.join(os.path.dirname(__file__), "kitten.jpg") geometry_options = {"tmargin": "1cm", "lmargin": "10cm"} doc = Document(geometry_options=geometry_options) - with doc.create(Section('The simple stuff')): - doc.append('Some regular text and some') - doc.append(italic('italic text. ')) - doc.append('\nAlso some crazy characters: $&#{}') - with doc.create(Subsection('Math that is incorrect')): - doc.append(Math(data=['2*3', '=', 9])) + with doc.create(Section("The simple stuff")): + doc.append("Some regular text and some") + doc.append(italic("italic text. ")) + doc.append("\nAlso some crazy characters: $&#{}") + with doc.create(Subsection("Math that is incorrect")): + doc.append(Math(data=["2*3", "=", 9])) - with doc.create(Subsection('Table of something')): - with doc.create(Tabular('rc|cl')) as table: + with doc.create(Subsection("Table of something")): + with doc.create(Tabular("rc|cl")) as table: table.add_hline() table.add_row((1, 2, 3, 4)) table.add_hline(1, 2) @@ -39,24 +51,22 @@ table.add_row((4, 5, 6, 7)) a = np.array([[100, 10, 20]]).T - M = np.matrix([[2, 3, 4], - [0, 0, 1], - [0, 0, 2]]) + M = np.matrix([[2, 3, 4], [0, 0, 1], [0, 0, 2]]) - with doc.create(Section('The fancy stuff')): - with doc.create(Subsection('Correct matrix equations')): - doc.append(Math(data=[Matrix(M), Matrix(a), '=', Matrix(M * a)])) + with doc.create(Section("The fancy stuff")): + with doc.create(Subsection("Correct matrix equations")): + doc.append(Math(data=[Matrix(M), Matrix(a), "=", Matrix(M * a)])) - with doc.create(Subsection('Alignat math environment')): + with doc.create(Subsection("Alignat math environment")): with doc.create(Alignat(numbering=False, escape=False)) as agn: - agn.append(r'\frac{a}{b} &= 0 \\') - agn.extend([Matrix(M), Matrix(a), '&=', Matrix(M * a)]) + agn.append(r"\frac{a}{b} &= 0 \\") + agn.extend([Matrix(M), Matrix(a), "&=", Matrix(M * a)]) - with doc.create(Subsection('Beautiful graphs')): + with doc.create(Subsection("Beautiful graphs")): with doc.create(TikZ()): - plot_options = 'height=4cm, width=6cm, grid=major' + plot_options = "height=4cm, width=6cm, grid=major" with doc.create(Axis(options=plot_options)) as plot: - plot.append(Plot(name='model', func='-x^5 - 242')) + plot.append(Plot(name="model", func="-x^5 - 242")) coordinates = [ (-4.77778, 2027.60977), @@ -70,11 +80,11 @@ (5.00000, -3269.56775), ] - plot.append(Plot(name='estimate', coordinates=coordinates)) + plot.append(Plot(name="estimate", coordinates=coordinates)) - with doc.create(Subsection('Cute kitten pictures')): - with doc.create(Figure(position='h!')) as kitten_pic: - kitten_pic.add_image(image_filename, width='120px') - kitten_pic.add_caption('Look it\'s on its back') + with doc.create(Subsection("Cute kitten pictures")): + with doc.create(Figure(position="h!")) as kitten_pic: + kitten_pic.add_image(image_filename, width="120px") + kitten_pic.add_caption("Look it's on its back") - doc.generate_pdf('full', clean_tex=False) + doc.generate_pdf("full", clean_tex=False) diff --git a/examples/header.py b/examples/header.py index 01d353b1..440ee493 100644 --- a/examples/header.py +++ b/examples/header.py @@ -9,8 +9,17 @@ """ # begin-doc-include -from pylatex import Document, PageStyle, Head, MiniPage, Foot, LargeText, \ - MediumText, LineBreak, simple_page_number +from pylatex import ( + Document, + Foot, + Head, + LargeText, + LineBreak, + MediumText, + MiniPage, + PageStyle, + simple_page_number, +) from pylatex.utils import bold @@ -44,11 +53,12 @@ def generate_header(): doc.change_document_style("header") # Add Heading - with doc.create(MiniPage(align='c')): + with doc.create(MiniPage(align="c")): doc.append(LargeText(bold("Title"))) doc.append(LineBreak()) doc.append(MediumText(bold("As at:"))) doc.generate_pdf("header", clean_tex=False) + generate_header() diff --git a/examples/lists.py b/examples/lists.py index cf674f2b..80cfca96 100644 --- a/examples/lists.py +++ b/examples/lists.py @@ -10,10 +10,17 @@ # begin-doc-include # Test for list structures in PyLaTeX. # More info @ http://en.wikibooks.org/wiki/LaTeX/List_Structures -from pylatex import Document, Section, Itemize, Enumerate, Description, \ - Command, NoEscape +from pylatex import ( + Command, + Description, + Document, + Enumerate, + Itemize, + NoEscape, + Section, +) -if __name__ == '__main__': +if __name__ == "__main__": doc = Document() # create a bulleted "itemize" list like the below: @@ -39,8 +46,9 @@ # \end{enumerate} with doc.create(Section('"Enumerate" list')): - with doc.create(Enumerate(enumeration_symbol=r"\alph*)", - options={'start': 20})) as enum: + with doc.create( + Enumerate(enumeration_symbol=r"\alph*)", options={"start": 20}) + ) as enum: enum.add_item("the first item") enum.add_item("the second item") enum.add_item(NoEscape("the third etc \\ldots")) @@ -58,4 +66,4 @@ desc.add_item("Second", "The second item") desc.add_item("Third", NoEscape("The third etc \\ldots")) - doc.generate_pdf('lists', clean_tex=False) + doc.generate_pdf("lists", clean_tex=False) diff --git a/examples/longtable.py b/examples/longtable.py index 655480ff..1ac991d2 100644 --- a/examples/longtable.py +++ b/examples/longtable.py @@ -13,32 +13,30 @@ def genenerate_longtabu(): - geometry_options = { - "margin": "2.54cm", - "includeheadfoot": True - } + geometry_options = {"margin": "2.54cm", "includeheadfoot": True} doc = Document(page_numbers=True, geometry_options=geometry_options) # Generate data table with doc.create(LongTable("l l l")) as data_table: - data_table.add_hline() - data_table.add_row(["header 1", "header 2", "header 3"]) - data_table.add_hline() - data_table.end_table_header() - data_table.add_hline() - data_table.add_row((MultiColumn(3, align='r', - data='Continued on Next Page'),)) - data_table.add_hline() - data_table.end_table_footer() - data_table.add_hline() - data_table.add_row((MultiColumn(3, align='r', - data='Not Continued on Next Page'),)) - data_table.add_hline() - data_table.end_table_last_footer() - row = ["Content1", "9", "Longer String"] - for i in range(150): - data_table.add_row(row) + data_table.add_hline() + data_table.add_row(["header 1", "header 2", "header 3"]) + data_table.add_hline() + data_table.end_table_header() + data_table.add_hline() + data_table.add_row((MultiColumn(3, align="r", data="Continued on Next Page"),)) + data_table.add_hline() + data_table.end_table_footer() + data_table.add_hline() + data_table.add_row( + (MultiColumn(3, align="r", data="Not Continued on Next Page"),) + ) + data_table.add_hline() + data_table.end_table_last_footer() + row = ["Content1", "9", "Longer String"] + for i in range(150): + data_table.add_row(row) doc.generate_pdf("longtable", clean_tex=False) + genenerate_longtabu() diff --git a/examples/matplotlib_ex.py b/examples/matplotlib_ex.py index fe1254f8..91761631 100755 --- a/examples/matplotlib_ex.py +++ b/examples/matplotlib_ex.py @@ -9,9 +9,9 @@ # begin-doc-include import matplotlib -from pylatex import Document, Section, Figure, NoEscape +from pylatex import Document, Figure, NoEscape, Section -matplotlib.use('Agg') # Not to use X server. For TravisCI. +matplotlib.use("Agg") # Not to use X server. For TravisCI. import matplotlib.pyplot as plt # noqa @@ -19,27 +19,27 @@ def main(fname, width, *args, **kwargs): geometry_options = {"right": "2cm", "left": "2cm"} doc = Document(fname, geometry_options=geometry_options) - doc.append('Introduction.') + doc.append("Introduction.") - with doc.create(Section('I am a section')): - doc.append('Take a look at this beautiful plot:') + with doc.create(Section("I am a section")): + doc.append("Take a look at this beautiful plot:") - with doc.create(Figure(position='htbp')) as plot: + with doc.create(Figure(position="htbp")) as plot: plot.add_plot(width=NoEscape(width), *args, **kwargs) - plot.add_caption('I am a caption.') + plot.add_caption("I am a caption.") - doc.append('Created using matplotlib.') + doc.append("Created using matplotlib.") - doc.append('Conclusion.') + doc.append("Conclusion.") doc.generate_pdf(clean_tex=False) -if __name__ == '__main__': +if __name__ == "__main__": x = [0, 1, 2, 3, 4, 5, 6] y = [15, 2, 7, 1, 5, 6, 9] plt.plot(x, y) - main('matplotlib_ex-dpi', r'1\textwidth', dpi=300) - main('matplotlib_ex-facecolor', r'0.5\textwidth', facecolor='b') + main("matplotlib_ex-dpi", r"1\textwidth", dpi=300) + main("matplotlib_ex-facecolor", r"0.5\textwidth", facecolor="b") diff --git a/examples/minipage.py b/examples/minipage.py index 0482e489..7da126ed 100644 --- a/examples/minipage.py +++ b/examples/minipage.py @@ -9,7 +9,7 @@ """ # begin-doc-include -from pylatex import Document, MiniPage, LineBreak, VerticalSpace +from pylatex import Document, LineBreak, MiniPage, VerticalSpace def generate_labels(): diff --git a/examples/multirow.py b/examples/multirow.py index 06fa5e0a..e145b308 100755 --- a/examples/multirow.py +++ b/examples/multirow.py @@ -7,62 +7,62 @@ """ # begin-doc-include -from pylatex import Document, Section, Subsection, Tabular, MultiColumn,\ - MultiRow +from pylatex import Document, MultiColumn, MultiRow, Section, Subsection, Tabular doc = Document("multirow") -section = Section('Multirow Test') +section = Section("Multirow Test") -test1 = Subsection('MultiColumn') -test2 = Subsection('MultiRow') -test3 = Subsection('MultiColumn and MultiRow') -test4 = Subsection('Vext01') +test1 = Subsection("MultiColumn") +test2 = Subsection("MultiRow") +test3 = Subsection("MultiColumn and MultiRow") +test4 = Subsection("Vext01") -table1 = Tabular('|c|c|c|c|') +table1 = Tabular("|c|c|c|c|") table1.add_hline() -table1.add_row((MultiColumn(4, align='|c|', data='Multicolumn'),)) +table1.add_row((MultiColumn(4, align="|c|", data="Multicolumn"),)) table1.add_hline() table1.add_row((1, 2, 3, 4)) table1.add_hline() table1.add_row((5, 6, 7, 8)) table1.add_hline() -row_cells = ('9', MultiColumn(3, align='|c|', data='Multicolumn not on left')) +row_cells = ("9", MultiColumn(3, align="|c|", data="Multicolumn not on left")) table1.add_row(row_cells) table1.add_hline() -table2 = Tabular('|c|c|c|') +table2 = Tabular("|c|c|c|") table2.add_hline() -table2.add_row((MultiRow(3, data='Multirow'), 1, 2)) +table2.add_row((MultiRow(3, data="Multirow"), 1, 2)) table2.add_hline(2, 3) -table2.add_row(('', 3, 4)) +table2.add_row(("", 3, 4)) table2.add_hline(2, 3) -table2.add_row(('', 5, 6)) +table2.add_row(("", 5, 6)) table2.add_hline() -table2.add_row((MultiRow(3, data='Multirow2'), '', '')) +table2.add_row((MultiRow(3, data="Multirow2"), "", "")) table2.add_empty_row() table2.add_empty_row() table2.add_hline() -table3 = Tabular('|c|c|c|') +table3 = Tabular("|c|c|c|") table3.add_hline() -table3.add_row((MultiColumn(2, align='|c|', - data=MultiRow(2, data='multi-col-row')), 'X')) -table3.add_row((MultiColumn(2, align='|c|', data=''), 'X')) +table3.add_row( + (MultiColumn(2, align="|c|", data=MultiRow(2, data="multi-col-row")), "X") +) +table3.add_row((MultiColumn(2, align="|c|", data=""), "X")) table3.add_hline() -table3.add_row(('X', 'X', 'X')) +table3.add_row(("X", "X", "X")) table3.add_hline() -table4 = Tabular('|c|c|c|') +table4 = Tabular("|c|c|c|") table4.add_hline() -col1_cell = MultiRow(4, data='span-4') -col2_cell = MultiRow(2, data='span-2') -table4.add_row((col1_cell, col2_cell, '3a')) +col1_cell = MultiRow(4, data="span-4") +col2_cell = MultiRow(2, data="span-2") +table4.add_row((col1_cell, col2_cell, "3a")) table4.add_hline(start=3) -table4.add_row(('', '', '3b')) +table4.add_row(("", "", "3b")) table4.add_hline(start=2) -table4.add_row(('', col2_cell, '3c')) +table4.add_row(("", col2_cell, "3c")) table4.add_hline(start=3) -table4.add_row(('', '', '3d')) +table4.add_row(("", "", "3d")) table4.add_hline() test1.append(table1) diff --git a/examples/numpy_ex.py b/examples/numpy_ex.py index b74521a1..3a449c45 100755 --- a/examples/numpy_ex.py +++ b/examples/numpy_ex.py @@ -9,38 +9,36 @@ # begin-doc-include import numpy as np -from pylatex import Document, Section, Subsection, Math, Matrix, VectorName +from pylatex import Document, Math, Matrix, Section, Subsection, VectorName -if __name__ == '__main__': +if __name__ == "__main__": a = np.array([[100, 10, 20]]).T doc = Document() - section = Section('Numpy tests') - subsection = Subsection('Array') + section = Section("Numpy tests") + subsection = Subsection("Array") vec = Matrix(a) - vec_name = VectorName('a') - math = Math(data=[vec_name, '=', vec]) + vec_name = VectorName("a") + math = Math(data=[vec_name, "=", vec]) subsection.append(math) section.append(subsection) - subsection = Subsection('Matrix') - M = np.matrix([[2, 3, 4], - [0, 0, 1], - [0, 0, 2]]) - matrix = Matrix(M, mtype='b') - math = Math(data=['M=', matrix]) + subsection = Subsection("Matrix") + M = np.matrix([[2, 3, 4], [0, 0, 1], [0, 0, 2]]) + matrix = Matrix(M, mtype="b") + math = Math(data=["M=", matrix]) subsection.append(math) section.append(subsection) - subsection = Subsection('Product') + subsection = Subsection("Product") - math = Math(data=['M', vec_name, '=', Matrix(M * a)]) + math = Math(data=["M", vec_name, "=", Matrix(M * a)]) subsection.append(math) section.append(subsection) doc.append(section) - doc.generate_pdf('numpy_ex', clean_tex=False) + doc.generate_pdf("numpy_ex", clean_tex=False) diff --git a/examples/own_commands_ex.py b/examples/own_commands_ex.py index 07654309..8895aa73 100644 --- a/examples/own_commands_ex.py +++ b/examples/own_commands_ex.py @@ -7,9 +7,9 @@ """ # begin-doc-include -from pylatex.base_classes import Environment, CommandBase, Arguments -from pylatex.package import Package from pylatex import Document, Section, UnsafeCommand +from pylatex.base_classes import Arguments, CommandBase, Environment +from pylatex.package import Package from pylatex.utils import NoEscape @@ -21,8 +21,8 @@ class ExampleEnvironment(Environment): ``exampleEnvironment``. """ - _latex_name = 'exampleEnvironment' - packages = [Package('mdframed')] + _latex_name = "exampleEnvironment" + packages = [Package("mdframed")] class ExampleCommand(CommandBase): @@ -33,56 +33,70 @@ class ExampleCommand(CommandBase): ``exampleCommand``. """ - _latex_name = 'exampleCommand' - packages = [Package('color')] + _latex_name = "exampleCommand" + packages = [Package("color")] # Create a new document doc = Document() -with doc.create(Section('Custom commands')): - doc.append(NoEscape( - r""" +with doc.create(Section("Custom commands")): + doc.append( + NoEscape( + r""" The following is a demonstration of a custom \LaTeX{} command with a couple of parameters. - """)) + """ + ) + ) # Define the new command - new_comm = UnsafeCommand('newcommand', '\exampleCommand', options=3, - extra_arguments=r'\color{#1} #2 #3 \color{black}') + new_comm = UnsafeCommand( + "newcommand", + "\exampleCommand", + options=3, + extra_arguments=r"\color{#1} #2 #3 \color{black}", + ) doc.append(new_comm) # Use our newly created command with different arguments - doc.append(ExampleCommand(arguments=Arguments('blue', 'Hello', 'World!'))) - doc.append(ExampleCommand(arguments=Arguments('green', 'Hello', 'World!'))) - doc.append(ExampleCommand(arguments=Arguments('red', 'Hello', 'World!'))) - -with doc.create(Section('Custom environments')): - doc.append(NoEscape( - r""" + doc.append(ExampleCommand(arguments=Arguments("blue", "Hello", "World!"))) + doc.append(ExampleCommand(arguments=Arguments("green", "Hello", "World!"))) + doc.append(ExampleCommand(arguments=Arguments("red", "Hello", "World!"))) + +with doc.create(Section("Custom environments")): + doc.append( + NoEscape( + r""" The following is a demonstration of a custom \LaTeX{} environment using the mdframed package. - """)) + """ + ) + ) # Define a style for our box - mdf_style_definition = UnsafeCommand('mdfdefinestyle', - arguments=['my_style', - ('linecolor=#1,' - 'linewidth=#2,' - 'leftmargin=1cm,' - 'leftmargin=1cm')]) + mdf_style_definition = UnsafeCommand( + "mdfdefinestyle", + arguments=[ + "my_style", + ("linecolor=#1," "linewidth=#2," "leftmargin=1cm," "leftmargin=1cm"), + ], + ) # Define the new environment using the style definition above - new_env = UnsafeCommand('newenvironment', 'exampleEnvironment', options=2, - extra_arguments=[ - mdf_style_definition.dumps() + - r'\begin{mdframed}[style=my_style]', - r'\end{mdframed}']) + new_env = UnsafeCommand( + "newenvironment", + "exampleEnvironment", + options=2, + extra_arguments=[ + mdf_style_definition.dumps() + r"\begin{mdframed}[style=my_style]", + r"\end{mdframed}", + ], + ) doc.append(new_env) # Usage of the newly created environment - with doc.create( - ExampleEnvironment(arguments=Arguments('red', 3))) as environment: - environment.append('This is the actual content') + with doc.create(ExampleEnvironment(arguments=Arguments("red", 3))) as environment: + environment.append("This is the actual content") # Generate pdf -doc.generate_pdf('own_commands_ex', clean_tex=False) +doc.generate_pdf("own_commands_ex", clean_tex=False) diff --git a/examples/quantities_ex.py b/examples/quantities_ex.py index 6d2c8147..ce30f2c8 100644 --- a/examples/quantities_ex.py +++ b/examples/quantities_ex.py @@ -9,39 +9,46 @@ # begin-doc-include import quantities as pq -from pylatex import Document, Section, Subsection, Math, Quantity +from pylatex import Document, Math, Quantity, Section, Subsection -if __name__ == '__main__': +if __name__ == "__main__": doc = Document() - section = Section('Quantity tests') + section = Section("Quantity tests") - subsection = Subsection('Scalars with units') + subsection = Subsection("Scalars with units") G = pq.constants.Newtonian_constant_of_gravitation moon_earth_distance = 384400 * pq.km moon_mass = 7.34767309e22 * pq.kg earth_mass = 5.972e24 * pq.kg moon_earth_force = G * moon_mass * earth_mass / moon_earth_distance**2 - q1 = Quantity(moon_earth_force.rescale(pq.newton), - options={'round-precision': 4, 'round-mode': 'figures'}) - math = Math(data=['F=', q1]) + q1 = Quantity( + moon_earth_force.rescale(pq.newton), + options={"round-precision": 4, "round-mode": "figures"}, + ) + math = Math(data=["F=", q1]) subsection.append(math) section.append(subsection) - subsection = Subsection('Scalars without units') + subsection = Subsection("Scalars without units") world_population = 7400219037 - N = Quantity(world_population, options={'round-precision': 2, - 'round-mode': 'figures'}, - format_cb="{0:23.17e}".format) - subsection.append(Math(data=['N=', N])) + N = Quantity( + world_population, + options={"round-precision": 2, "round-mode": "figures"}, + format_cb="{0:23.17e}".format, + ) + subsection.append(Math(data=["N=", N])) section.append(subsection) - subsection = Subsection('Scalars with uncertainties') - width = pq.UncertainQuantity(7.0, pq.meter, .4) - length = pq.UncertainQuantity(6.0, pq.meter, .3) - area = Quantity(width * length, options='separate-uncertainty', - format_cb=lambda x: "{0:.1f}".format(float(x))) - subsection.append(Math(data=['A=', area])) + subsection = Subsection("Scalars with uncertainties") + width = pq.UncertainQuantity(7.0, pq.meter, 0.4) + length = pq.UncertainQuantity(6.0, pq.meter, 0.3) + area = Quantity( + width * length, + options="separate-uncertainty", + format_cb=lambda x: "{0:.1f}".format(float(x)), + ) + subsection.append(Math(data=["A=", area])) section.append(subsection) doc.append(section) - doc.generate_pdf('quantities_ex', clean_tex=False) + doc.generate_pdf("quantities_ex", clean_tex=False) diff --git a/examples/subfigure.py b/examples/subfigure.py index d3471c33..b3c53557 100644 --- a/examples/subfigure.py +++ b/examples/subfigure.py @@ -7,29 +7,26 @@ """ # begin-doc-include -from pylatex import Document, Section, Figure, SubFigure, NoEscape import os -if __name__ == '__main__': - doc = Document(default_filepath='subfigures') - image_filename = os.path.join(os.path.dirname(__file__), 'kitten.jpg') +from pylatex import Document, Figure, NoEscape, Section, SubFigure - with doc.create(Section('Showing subfigures')): - with doc.create(Figure(position='h!')) as kittens: - with doc.create(SubFigure( - position='b', - width=NoEscape(r'0.45\linewidth'))) as left_kitten: +if __name__ == "__main__": + doc = Document(default_filepath="subfigures") + image_filename = os.path.join(os.path.dirname(__file__), "kitten.jpg") - left_kitten.add_image(image_filename, - width=NoEscape(r'\linewidth')) - left_kitten.add_caption('Kitten on the left') - with doc.create(SubFigure( - position='b', - width=NoEscape(r'0.45\linewidth'))) as right_kitten: - - right_kitten.add_image(image_filename, - width=NoEscape(r'\linewidth')) - right_kitten.add_caption('Kitten on the right') + with doc.create(Section("Showing subfigures")): + with doc.create(Figure(position="h!")) as kittens: + with doc.create( + SubFigure(position="b", width=NoEscape(r"0.45\linewidth")) + ) as left_kitten: + left_kitten.add_image(image_filename, width=NoEscape(r"\linewidth")) + left_kitten.add_caption("Kitten on the left") + with doc.create( + SubFigure(position="b", width=NoEscape(r"0.45\linewidth")) + ) as right_kitten: + right_kitten.add_image(image_filename, width=NoEscape(r"\linewidth")) + right_kitten.add_caption("Kitten on the right") kittens.add_caption("Two kittens") doc.generate_pdf(clean_tex=False) diff --git a/examples/textblock.py b/examples/textblock.py index e8dab60c..f5e19700 100644 --- a/examples/textblock.py +++ b/examples/textblock.py @@ -10,8 +10,16 @@ """ # begin-doc-include -from pylatex import Document, MiniPage, TextBlock, MediumText, HugeText, \ - SmallText, VerticalSpace, HorizontalSpace +from pylatex import ( + Document, + HorizontalSpace, + HugeText, + MediumText, + MiniPage, + SmallText, + TextBlock, + VerticalSpace, +) from pylatex.utils import bold geometry_options = {"margin": "0.5in"} diff --git a/examples/tikzdraw.py b/examples/tikzdraw.py index f9da6cd3..8b8305c2 100644 --- a/examples/tikzdraw.py +++ b/examples/tikzdraw.py @@ -7,56 +7,56 @@ """ # begin-doc-include -from pylatex import (Document, TikZ, TikZNode, - TikZDraw, TikZCoordinate, - TikZUserPath, TikZOptions) - -if __name__ == '__main__': - +from pylatex import ( + Document, + TikZ, + TikZCoordinate, + TikZDraw, + TikZNode, + TikZOptions, + TikZUserPath, +) + +if __name__ == "__main__": # create document doc = Document() # add our sample drawings with doc.create(TikZ()) as pic: - # options for our node - node_kwargs = {'align': 'center', - 'minimum size': '100pt', - 'fill': 'black!20'} + node_kwargs = {"align": "center", "minimum size": "100pt", "fill": "black!20"} # create our test node - box = TikZNode(text='My block', - handle='box', - at=TikZCoordinate(0, 0), - options=TikZOptions('draw', - 'rounded corners', - **node_kwargs)) + box = TikZNode( + text="My block", + handle="box", + at=TikZCoordinate(0, 0), + options=TikZOptions("draw", "rounded corners", **node_kwargs), + ) # add to tikzpicture pic.append(box) # draw a few paths - pic.append(TikZDraw([TikZCoordinate(0, -6), - 'rectangle', - TikZCoordinate(2, -8)], - options=TikZOptions(fill='red'))) + pic.append( + TikZDraw( + [TikZCoordinate(0, -6), "rectangle", TikZCoordinate(2, -8)], + options=TikZOptions(fill="red"), + ) + ) # show use of anchor, relative coordinate - pic.append(TikZDraw([box.west, - '--', - '++(-1,0)'])) + pic.append(TikZDraw([box.west, "--", "++(-1,0)"])) # demonstrate the use of the with syntax with pic.create(TikZDraw()) as path: - # start at an anchor of the node path.append(box.east) # necessary here because 'in' is a python keyword - path_options = {'in': 90, 'out': 0} - path.append(TikZUserPath('edge', - TikZOptions('-latex', **path_options))) + path_options = {"in": 90, "out": 0} + path.append(TikZUserPath("edge", TikZOptions("-latex", **path_options))) path.append(TikZCoordinate(1, 0, relative=True)) - doc.generate_pdf('tikzdraw', clean_tex=False) + doc.generate_pdf("tikzdraw", clean_tex=False) diff --git a/pylatex/__init__.py b/pylatex/__init__.py index f76706e0..03c16e05 100644 --- a/pylatex/__init__.py +++ b/pylatex/__init__.py @@ -5,28 +5,66 @@ :license: MIT, see License for more details. """ -from .basic import HugeText, NewPage, LineBreak, NewLine, HFill, LargeText, \ - MediumText, SmallText, FootnoteText, TextColor +from . import _version +from .base_classes import Command, UnsafeCommand +from .basic import ( + FootnoteText, + HFill, + HugeText, + LargeText, + LineBreak, + MediumText, + NewLine, + NewPage, + SmallText, + TextColor, +) from .document import Document -from .frames import MdFramed, FBox -from .math import Math, VectorName, Matrix, Alignat +from .errors import TableRowSizeError +from .figure import Figure, StandAloneGraphic, SubFigure +from .frames import FBox, MdFramed +from .headfoot import Foot, Head, PageStyle, simple_page_number +from .labelref import Autoref, Eqref, Hyperref, Label, Marker, Pageref, Ref +from .lists import Description, Enumerate, Itemize +from .math import Alignat, Math, Matrix, VectorName from .package import Package -from .section import Chapter, Section, Subsection, Subsubsection -from .table import Table, MultiColumn, MultiRow, Tabular, Tabu, LongTable, \ - LongTabu, Tabularx, LongTabularx, ColumnType -from .tikz import TikZ, Axis, Plot, TikZNode, TikZDraw, TikZCoordinate, \ - TikZPathList, TikZPath, TikZUserPath, TikZOptions, TikZNodeAnchor, \ - TikZScope -from .figure import Figure, SubFigure, StandAloneGraphic -from .lists import Enumerate, Itemize, Description +from .position import ( + Center, + FlushLeft, + FlushRight, + HorizontalSpace, + MiniPage, + TextBlock, + VerticalSpace, +) from .quantities import Quantity -from .base_classes import Command, UnsafeCommand +from .section import Chapter, Section, Subsection, Subsubsection +from .table import ( + ColumnType, + LongTable, + LongTabu, + LongTabularx, + MultiColumn, + MultiRow, + Table, + Tabu, + Tabular, + Tabularx, +) +from .tikz import ( + Axis, + Plot, + TikZ, + TikZCoordinate, + TikZDraw, + TikZNode, + TikZNodeAnchor, + TikZOptions, + TikZPath, + TikZPathList, + TikZScope, + TikZUserPath, +) from .utils import NoEscape, escape_latex -from .errors import TableRowSizeError -from .headfoot import PageStyle, Head, Foot, simple_page_number -from .position import Center, FlushLeft, FlushRight, MiniPage, TextBlock, \ - HorizontalSpace, VerticalSpace -from .labelref import Marker, Label, Ref, Pageref, Eqref, Autoref, Hyperref -from . import _version -__version__ = _version.get_versions()['version'] +__version__ = _version.get_versions()["version"] diff --git a/pylatex/_version.py b/pylatex/_version.py index 51858ef6..a8b1434c 100644 --- a/pylatex/_version.py +++ b/pylatex/_version.py @@ -1,4 +1,3 @@ - # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -12,12 +11,12 @@ """Git implementation of _version.py.""" import errno +import functools import os import re import subprocess import sys from typing import Any, Callable, Dict, List, Optional, Tuple -import functools def get_keywords() -> Dict[str, str]: @@ -68,12 +67,14 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f: Callable) -> Callable: """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate @@ -100,10 +101,14 @@ def run_command( try: dispcmd = str([command] + args) # remember shell=False, so use git.cmd on windows, not just git - process = subprocess.Popen([command] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None), **popen_kwargs) + process = subprocess.Popen( + [command] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + **popen_kwargs, + ) break except OSError as e: if e.errno == errno.ENOENT: @@ -141,15 +146,21 @@ def versions_from_parentdir( for _ in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @@ -212,7 +223,7 @@ def git_versions_from_keywords( # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} + tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -221,7 +232,7 @@ def git_versions_from_keywords( # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = {r for r in refs if re.search(r'\d', r)} + tags = {r for r in refs if re.search(r"\d", r)} if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: @@ -229,32 +240,36 @@ def git_versions_from_keywords( for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] # Filter out refs that exactly match prefix or that don't start # with a number once the prefix is stripped (mostly a concern # when prefix is '') - if not re.match(r'\d', r): + if not re.match(r"\d", r): continue if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs( - tag_prefix: str, - root: str, - verbose: bool, - runner: Callable = run_command + tag_prefix: str, root: str, verbose: bool, runner: Callable = run_command ) -> Dict[str, Any]: """Get version from 'git describe' in the root of the source tree. @@ -273,8 +288,7 @@ def git_pieces_from_vcs( env.pop("GIT_DIR", None) runner = functools.partial(runner, env=env) - _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=not verbose) + _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=not verbose) if rc != 0: if verbose: print("Directory %s not under git control" % root) @@ -282,10 +296,19 @@ def git_pieces_from_vcs( # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = runner(GITS, [ - "describe", "--tags", "--dirty", "--always", "--long", - "--match", f"{tag_prefix}[[:digit:]]*" - ], cwd=root) + describe_out, rc = runner( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + f"{tag_prefix}[[:digit:]]*", + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") @@ -300,8 +323,7 @@ def git_pieces_from_vcs( pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None - branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], - cwd=root) + branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) # --abbrev-ref was added in git-1.6.3 if rc != 0 or branch_name is None: raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") @@ -341,17 +363,16 @@ def git_pieces_from_vcs( dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparsable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out return pieces # tag @@ -360,10 +381,12 @@ def git_pieces_from_vcs( if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -412,8 +435,7 @@ def render_pep440(pieces: Dict[str, Any]) -> str: rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -442,8 +464,7 @@ def render_pep440_branch(pieces: Dict[str, Any]) -> str: rendered = "0" if pieces["branch"] != "master": rendered += ".dev0" - rendered += "+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -604,11 +625,13 @@ def render_git_describe_long(pieces: Dict[str, Any]) -> str: def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]: """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -632,9 +655,13 @@ def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]: else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } def get_versions() -> Dict[str, Any]: @@ -648,8 +675,7 @@ def get_versions() -> Dict[str, Any]: verbose = cfg.verbose try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) except NotThisMethod: pass @@ -658,13 +684,16 @@ def get_versions() -> Dict[str, Any]: # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. - for _ in cfg.versionfile_source.split('/'): + for _ in cfg.versionfile_source.split("/"): root = os.path.dirname(root) except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None, + } try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) @@ -678,6 +707,10 @@ def get_versions() -> Dict[str, Any]: except NotThisMethod: pass - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } diff --git a/pylatex/base_classes/__init__.py b/pylatex/base_classes/__init__.py index 54ce7cae..8d62e216 100644 --- a/pylatex/base_classes/__init__.py +++ b/pylatex/base_classes/__init__.py @@ -5,11 +5,18 @@ :license: MIT, see License for more details. """ -from .latex_object import LatexObject -from .containers import Container, Environment, ContainerCommand -from .command import CommandBase, Command, UnsafeCommand, Options, \ - SpecialOptions, Arguments, SpecialArguments +from .command import ( + Arguments, + Command, + CommandBase, + Options, + SpecialArguments, + SpecialOptions, + UnsafeCommand, +) +from .containers import Container, ContainerCommand, Environment from .float import Float +from .latex_object import LatexObject # Old names of the base classes for backwards compatibility BaseLaTeXClass = LatexObject diff --git a/pylatex/base_classes/command.py b/pylatex/base_classes/command.py index f9cea00b..db2bab3f 100644 --- a/pylatex/base_classes/command.py +++ b/pylatex/base_classes/command.py @@ -11,8 +11,8 @@ from reprlib import recursive_repr -from .latex_object import LatexObject from ..utils import dumps_list +from .latex_object import LatexObject class CommandBase(LatexObject): @@ -23,8 +23,7 @@ class CommandBase(LatexObject): """ - def __init__(self, arguments=None, options=None, *, - extra_arguments=None): + def __init__(self, arguments=None, options=None, *, extra_arguments=None): r""" Args ---- @@ -40,17 +39,17 @@ def __init__(self, arguments=None, options=None, *, """ - self._set_parameters(arguments, 'arguments') - self._set_parameters(options, 'options') + self._set_parameters(arguments, "arguments") + self._set_parameters(options, "options") if extra_arguments is None: self.extra_arguments = None else: - self._set_parameters(extra_arguments, 'extra_arguments') + self._set_parameters(extra_arguments, "extra_arguments") super().__init__() def _set_parameters(self, parameters, argument_type): - parameter_cls = Options if argument_type == 'options' else Arguments + parameter_cls = Options if argument_type == "options" else Arguments if parameters is None: parameters = parameter_cls() @@ -70,8 +69,7 @@ def __key(self): tuple """ - return (self.latex_name, self.arguments, self.options, - self.extra_arguments) + return (self.latex_name, self.arguments, self.options, self.extra_arguments) def __eq__(self, other): """Compare two commands. @@ -117,15 +115,18 @@ def dumps(self): arguments = self.arguments.dumps() if self.extra_arguments is None: - return r'\{command}{options}{arguments}'\ - .format(command=self.latex_name, options=options, - arguments=arguments) + return r"\{command}{options}{arguments}".format( + command=self.latex_name, options=options, arguments=arguments + ) extra_arguments = self.extra_arguments.dumps() - return r'\{command}{arguments}{options}{extra_arguments}'\ - .format(command=self.latex_name, arguments=arguments, - options=options, extra_arguments=extra_arguments) + return r"\{command}{arguments}{options}{extra_arguments}".format( + command=self.latex_name, + arguments=arguments, + options=options, + extra_arguments=extra_arguments, + ) class Command(CommandBase): @@ -135,10 +136,17 @@ class Command(CommandBase): is used multiple times it is better to subclass `.CommandBase`. """ - _repr_attributes_mapping = {'command': 'latex_name'} - - def __init__(self, command=None, arguments=None, options=None, *, - extra_arguments=None, packages=None): + _repr_attributes_mapping = {"command": "latex_name"} + + def __init__( + self, + command=None, + arguments=None, + options=None, + *, + extra_arguments=None, + packages=None + ): r""" Args ---- @@ -207,7 +215,7 @@ class Parameters(LatexObject): def __repr__(self): args = [repr(a) for a in self._positional_args] args += ["%s=%r" % k_v for k_v in self._key_value_args.items()] - return self.__class__.__name__ + '(' + ', '.join(args) + ')' + return self.__class__.__name__ + "(" + ", ".join(args) + ")" def __init__(self, *args, **kwargs): r""" @@ -220,10 +228,10 @@ def __init__(self, *args, **kwargs): """ if len(args) == 1 and not isinstance(args[0], str): - if hasattr(args[0], 'items') and len(kwargs) == 0: + if hasattr(args[0], "items") and len(kwargs) == 0: kwargs = args[0] # do not just iterate over the dict keys args = () - elif hasattr(args[0], '__iter__'): + elif hasattr(args[0], "__iter__"): args = args[0] self._positional_args = list(args) @@ -281,10 +289,11 @@ def _format_contents(self, prefix, separator, suffix): params = self._list_args_kwargs() if len(params) <= 0: - return '' + return "" - string = prefix + dumps_list(params, escape=self.escape, - token=separator) + suffix + string = ( + prefix + dumps_list(params, escape=self.escape, token=separator) + suffix + ) return string @@ -298,8 +307,9 @@ def _list_args_kwargs(self): params = [] params.extend(self._positional_args) - params.extend(['{k}={v}'.format(k=k, v=v) for k, v in - self._key_value_args.items()]) + params.extend( + ["{k}={v}".format(k=k, v=v) for k, v in self._key_value_args.items()] + ) return params @@ -333,7 +343,7 @@ def dumps(self): str """ - return self._format_contents('[', ',', ']') + return self._format_contents("[", ",", "]") class SpecialOptions(Options): @@ -342,7 +352,7 @@ class SpecialOptions(Options): def dumps(self): """Represent the parameters as a string in LaTex syntax.""" - return self._format_contents('[', '][', ']') + return self._format_contents("[", "][", "]") class Arguments(Parameters): @@ -375,7 +385,7 @@ def dumps(self): str """ - return self._format_contents('{', '}{', '}') + return self._format_contents("{", "}{", "}") class SpecialArguments(Arguments): @@ -391,4 +401,4 @@ def dumps(self): str """ - return self._format_contents('{', ',', '}') + return self._format_contents("{", ",", "}") diff --git a/pylatex/base_classes/containers.py b/pylatex/base_classes/containers.py index 191485e2..6f547338 100644 --- a/pylatex/base_classes/containers.py +++ b/pylatex/base_classes/containers.py @@ -7,10 +7,12 @@ """ from collections import UserList -from pylatex.utils import dumps_list from contextlib import contextmanager + +from pylatex.utils import dumps_list + +from .command import Arguments, Command from .latex_object import LatexObject -from .command import Command, Arguments class Container(LatexObject, UserList): @@ -23,7 +25,7 @@ class Container(LatexObject, UserList): """ - content_separator = '%\n' + content_separator = "%\n" def __init__(self, *, data=None): r""" @@ -48,7 +50,7 @@ def __init__(self, *, data=None): @property def _repr_attributes(self): - return super()._repr_attributes + ['real_data'] + return super()._repr_attributes + ["real_data"] def dumps_content(self, **kwargs): r"""Represent the container as a string in LaTeX syntax. @@ -65,8 +67,9 @@ def dumps_content(self, **kwargs): A LaTeX string representing the container """ - return dumps_list(self, escape=self.escape, - token=self.content_separator, **kwargs) + return dumps_list( + self, escape=self.escape, token=self.content_separator, **kwargs + ) def _propagate_packages(self): """Make sure packages get propagated.""" @@ -133,8 +136,7 @@ class Environment(Container): #: string if it has no content. omit_if_empty = False - def __init__(self, *, options=None, arguments=None, start_arguments=None, - **kwargs): + def __init__(self, *, options=None, arguments=None, start_arguments=None, **kwargs): r""" Args ---- @@ -165,9 +167,9 @@ def dumps(self): content = self.dumps_content() if not content.strip() and self.omit_if_empty: - return '' + return "" - string = '' + string = "" # Something other than None needs to be used as extra arguments, that # way the options end up behind the latex_name argument. @@ -176,14 +178,15 @@ def dumps(self): else: extra_arguments = self.arguments - begin = Command('begin', self.start_arguments, self.options, - extra_arguments=extra_arguments) + begin = Command( + "begin", self.start_arguments, self.options, extra_arguments=extra_arguments + ) begin.arguments._positional_args.insert(0, self.latex_name) string += begin.dumps() + self.content_separator string += content + self.content_separator - string += Command('end', self.latex_name).dumps() + string += Command("end", self.latex_name).dumps() return string @@ -255,18 +258,17 @@ def dumps(self): content = self.dumps_content() if not content.strip() and self.omit_if_empty: - return '' + return "" - string = '' + string = "" - start = Command(self.latex_name, arguments=self.arguments, - options=self.options) + start = Command(self.latex_name, arguments=self.arguments, options=self.options) - string += start.dumps() + '{%\n' + string += start.dumps() + "{%\n" - if content != '': - string += content + '%\n}' + if content != "": + string += content + "%\n}" else: - string += '}' + string += "}" return string diff --git a/pylatex/base_classes/float.py b/pylatex/base_classes/float.py index 1cae9acb..446f8222 100644 --- a/pylatex/base_classes/float.py +++ b/pylatex/base_classes/float.py @@ -6,7 +6,7 @@ :license: MIT, see License for more details. """ -from . import Environment, Command +from . import Command, Environment class Float(Environment): @@ -17,7 +17,7 @@ class Float(Environment): separate_paragraph = True _repr_attributes_mapping = { - 'position': 'options', + "position": "options", } def __init__(self, *, position=None, **kwargs): @@ -44,4 +44,4 @@ def add_caption(self, caption): The text of the caption. """ - self.append(Command('caption', caption)) + self.append(Command("caption", caption)) diff --git a/pylatex/base_classes/latex_object.py b/pylatex/base_classes/latex_object.py index 8c5631ed..2965ad52 100644 --- a/pylatex/base_classes/latex_object.py +++ b/pylatex/base_classes/latex_object.py @@ -6,11 +6,13 @@ :license: MIT, see License for more details. """ +from abc import ABCMeta, abstractmethod +from inspect import getfullargspec +from reprlib import recursive_repr + from ordered_set import OrderedSet + from ..utils import dumps_list -from abc import abstractmethod, ABCMeta -from reprlib import recursive_repr -from inspect import getfullargspec class _CreatePackages(ABCMeta): @@ -18,11 +20,11 @@ def __init__(cls, name, bases, d): # noqa packages = OrderedSet() for b in bases: - if hasattr(b, 'packages'): + if hasattr(b, "packages"): packages |= b.packages - if 'packages' in d: - packages |= d['packages'] + if "packages" in d: + packages |= d["packages"] cls.packages = packages @@ -39,7 +41,7 @@ class LatexObject(metaclass=_CreatePackages): """ _latex_name = None - _star_latex_name = False # latex_name + ('*' if True else '') + _star_latex_name = False # latex_name + ('*' if True else '') #: Set this to an iterable to override the list of default repr #: attributes. @@ -91,18 +93,23 @@ def __init__(self): def __repr__(self): """Create a printable representation of the object.""" - return self.__class__.__name__ + '(' + \ - ', '.join(map(repr, self._repr_values)) + ')' + return ( + self.__class__.__name__ + + "(" + + ", ".join(map(repr, self._repr_values)) + + ")" + ) @property def _repr_values(self): """Return values that are to be shown in repr string.""" + def getattr_better(obj, field): try: return getattr(obj, field) except AttributeError as e: try: - return getattr(obj, '_' + field) + return getattr(obj, "_" + field) except AttributeError: raise e @@ -127,7 +134,7 @@ def latex_name(self): It can be `None` when the class doesn't have a name. """ - star = ('*' if self._star_latex_name else '') + star = "*" if self._star_latex_name else "" if self._latex_name is not None: return self._latex_name + star return self.__class__.__name__.lower() + star @@ -165,7 +172,7 @@ def generate_tex(self, filepath): The name of the file (without .tex) """ - with open(filepath + '.tex', 'w', encoding='utf-8') as newf: + with open(filepath + ".tex", "w", encoding="utf-8") as newf: self.dump(newf) def dumps_packages(self): @@ -202,9 +209,9 @@ def dumps_as_content(self): string = self.dumps() if self.separate_paragraph or self.begin_paragraph: - string = '\n\n' + string.lstrip('\n') + string = "\n\n" + string.lstrip("\n") if self.separate_paragraph or self.end_paragraph: - string = string.rstrip('\n') + '\n\n' + string = string.rstrip("\n") + "\n\n" return string diff --git a/pylatex/basic.py b/pylatex/basic.py index e00ef708..780f1dd5 100644 --- a/pylatex/basic.py +++ b/pylatex/basic.py @@ -6,7 +6,7 @@ :license: MIT, see License for more details. """ -from .base_classes import CommandBase, Environment, ContainerCommand +from .base_classes import CommandBase, ContainerCommand, Environment from .package import Package @@ -69,9 +69,7 @@ class FootnoteText(HugeText): class TextColor(ContainerCommand): """An environment which changes the text color of the data.""" - _repr_attributes_mapping = { - "color": "arguments" - } + _repr_attributes_mapping = {"color": "arguments"} packages = [Package("xcolor")] diff --git a/pylatex/config.py b/pylatex/config.py index eb8f04d9..e2b2ce65 100644 --- a/pylatex/config.py +++ b/pylatex/config.py @@ -107,6 +107,7 @@ class Version2(Version1): microtype = True row_height = 1.3 + #: The default configuration in the nxt major release. Currently the same as #: `Version2`. NextMajor = Version2 diff --git a/pylatex/document.py b/pylatex/document.py index 47d4c73f..86c2d709 100644 --- a/pylatex/document.py +++ b/pylatex/document.py @@ -6,17 +6,25 @@ :license: MIT, see License for more details. """ +import errno import os -import sys import subprocess -import errno -from .base_classes import Environment, Command, Container, LatexObject, \ - UnsafeCommand, SpecialArguments -from .package import Package -from .errors import CompilerError -from .utils import dumps_list, rm_temp_dir, NoEscape +import sys + import pylatex.config as cf +from .base_classes import ( + Command, + Container, + Environment, + LatexObject, + SpecialArguments, + UnsafeCommand, +) +from .errors import CompilerError +from .package import Package +from .utils import NoEscape, dumps_list, rm_temp_dir + class Document(Environment): r""" @@ -28,11 +36,23 @@ class Document(Environment): """ - def __init__(self, default_filepath='default_filepath', *, - documentclass='article', document_options=None, fontenc='T1', - inputenc='utf8', font_size="normalsize", lmodern=True, - textcomp=True, microtype=None, page_numbers=True, indent=None, - geometry_options=None, data=None): + def __init__( + self, + default_filepath="default_filepath", + *, + documentclass="article", + document_options=None, + fontenc="T1", + inputenc="utf8", + font_size="normalsize", + lmodern=True, + textcomp=True, + microtype=None, + page_numbers=True, + indent=None, + geometry_options=None, + data=None + ): r""" Args ---- @@ -72,9 +92,9 @@ def __init__(self, default_filepath='default_filepath', *, if isinstance(documentclass, Command): self.documentclass = documentclass else: - self.documentclass = Command('documentclass', - arguments=documentclass, - options=document_options) + self.documentclass = Command( + "documentclass", arguments=documentclass, options=document_options + ) if indent is None: indent = cf.active.indent if microtype is None: @@ -90,35 +110,37 @@ def __init__(self, default_filepath='default_filepath', *, packages = [] if fontenc is not None: - packages.append(Package('fontenc', options=fontenc)) + packages.append(Package("fontenc", options=fontenc)) if inputenc is not None: - packages.append(Package('inputenc', options=inputenc)) + packages.append(Package("inputenc", options=inputenc)) if lmodern: - packages.append(Package('lmodern')) + packages.append(Package("lmodern")) if textcomp: - packages.append(Package('textcomp')) + packages.append(Package("textcomp")) if page_numbers: - packages.append(Package('lastpage')) + packages.append(Package("lastpage")) if not indent: - packages.append(Package('parskip')) + packages.append(Package("parskip")) if microtype: - packages.append(Package('microtype')) + packages.append(Package("microtype")) if geometry_options is not None: - packages.append(Package('geometry')) + packages.append(Package("geometry")) # Make sure we don't add this options command for an empty list, # because that breaks. if geometry_options: - packages.append(Command( - 'geometry', - arguments=SpecialArguments(geometry_options), - )) + packages.append( + Command( + "geometry", + arguments=SpecialArguments(geometry_options), + ) + ) super().__init__(data=data) # Usually the name is the class name, but if we create our own # document class, \begin{document} gets messed up. - self._latex_name = 'document' + self._latex_name = "document" self.packages |= packages self.variables = [] @@ -143,7 +165,7 @@ def _propagate_packages(self): super()._propagate_packages() - for item in (self.preamble): + for item in self.preamble: if isinstance(item, LatexObject): if isinstance(item, Container): item._propagate_packages() @@ -158,12 +180,12 @@ def dumps(self): str """ - head = self.documentclass.dumps() + '%\n' - head += self.dumps_packages() + '%\n' - head += dumps_list(self.variables) + '%\n' - head += dumps_list(self.preamble) + '%\n' + head = self.documentclass.dumps() + "%\n" + head += self.dumps_packages() + "%\n" + head += dumps_list(self.variables) + "%\n" + head += dumps_list(self.preamble) + "%\n" - return head + '%\n' + super().dumps() + return head + "%\n" + super().dumps() def generate_tex(self, filepath=None): """Generate a .tex file for the document. @@ -177,8 +199,16 @@ def generate_tex(self, filepath=None): super().generate_tex(self._select_filepath(filepath)) - def generate_pdf(self, filepath=None, *, clean=True, clean_tex=True, - compiler=None, compiler_args=None, silent=True): + def generate_pdf( + self, + filepath=None, + *, + clean=True, + clean_tex=True, + compiler=None, + compiler_args=None, + silent=True + ): """Generate a pdf file from the document. Args @@ -212,8 +242,7 @@ def generate_pdf(self, filepath=None, *, clean=True, clean_tex=True, filepath = self._select_filepath(filepath) if not os.path.basename(filepath): - filepath = os.path.join(os.path.abspath(filepath), - 'default_basename') + filepath = os.path.join(os.path.abspath(filepath), "default_basename") else: filepath = os.path.abspath(filepath) @@ -228,18 +257,15 @@ def generate_pdf(self, filepath=None, *, clean=True, clean_tex=True, if compiler is not None: compilers = ((compiler, []),) else: - latexmk_args = ['--pdf'] + latexmk_args = ["--pdf"] - compilers = ( - ('latexmk', latexmk_args), - ('pdflatex', []) - ) + compilers = (("latexmk", latexmk_args), ("pdflatex", [])) - main_arguments = ['--interaction=nonstopmode', filepath + '.tex'] + main_arguments = ["--interaction=nonstopmode", filepath + ".tex"] check_output_kwargs = {} if python_cwd_available: - check_output_kwargs = {'cwd': dest_dir} + check_output_kwargs = {"cwd": dest_dir} os_error = None @@ -247,9 +273,9 @@ def generate_pdf(self, filepath=None, *, clean=True, clean_tex=True, command = [compiler] + arguments + compiler_args + main_arguments try: - output = subprocess.check_output(command, - stderr=subprocess.STDOUT, - **check_output_kwargs) + output = subprocess.check_output( + command, stderr=subprocess.STDOUT, **check_output_kwargs + ) except (OSError, IOError) as e: # Use FileNotFoundError when python 2 is dropped os_error = e @@ -269,17 +295,18 @@ def generate_pdf(self, filepath=None, *, clean=True, clean_tex=True, if clean: try: # Try latexmk cleaning first - subprocess.check_output(['latexmk', '-c', filepath], - stderr=subprocess.STDOUT, - **check_output_kwargs) + subprocess.check_output( + ["latexmk", "-c", filepath], + stderr=subprocess.STDOUT, + **check_output_kwargs + ) except (OSError, IOError, subprocess.CalledProcessError): # Otherwise just remove some file extensions. - extensions = ['aux', 'log', 'out', 'fls', - 'fdb_latexmk'] + extensions = ["aux", "log", "out", "fls", "fdb_latexmk"] for ext in extensions: try: - os.remove(filepath + '.' + ext) + os.remove(filepath + "." + ext) except (OSError, IOError) as e: # Use FileNotFoundError when python 2 is dropped if e.errno != errno.ENOENT: @@ -287,7 +314,7 @@ def generate_pdf(self, filepath=None, *, clean=True, clean_tex=True, rm_temp_dir() if clean_tex: - os.remove(filepath + '.tex') # Remove generated tex file + os.remove(filepath + ".tex") # Remove generated tex file # Compilation has finished, so no further compilers have to be # tried @@ -295,11 +322,13 @@ def generate_pdf(self, filepath=None, *, clean=True, clean_tex=True, else: # Notify user that none of the compilers worked. - raise(CompilerError( - 'No LaTex compiler was found\n' - 'Either specify a LaTex compiler ' - 'or make sure you have latexmk or pdfLaTex installed.' - )) + raise ( + CompilerError( + "No LaTex compiler was found\n" + "Either specify a LaTex compiler " + "or make sure you have latexmk or pdfLaTex installed." + ) + ) if not python_cwd_available: os.chdir(cur_dir) @@ -321,9 +350,10 @@ def _select_filepath(self, filepath): if filepath is None: return self.default_filepath else: - if os.path.basename(filepath) == '': - filepath = os.path.join(filepath, os.path.basename( - self.default_filepath)) + if os.path.basename(filepath) == "": + filepath = os.path.join( + filepath, os.path.basename(self.default_filepath) + ) return filepath def change_page_style(self, style): @@ -365,9 +395,9 @@ def add_color(self, name, model, description): self.packages.append(Package("color")) self.color = True - self.preamble.append(Command("definecolor", arguments=[name, - model, - description])) + self.preamble.append( + Command("definecolor", arguments=[name, model, description]) + ) def change_length(self, parameter, value): r"""Change the length of a certain parameter to a certain value. @@ -380,8 +410,7 @@ def change_length(self, parameter, value): The value to set the parameter to """ - self.preamble.append(UnsafeCommand('setlength', - arguments=[parameter, value])) + self.preamble.append(UnsafeCommand("setlength", arguments=[parameter, value])) def set_variable(self, name, value): r"""Add a variable which can be used inside the document. @@ -407,10 +436,10 @@ def set_variable(self, name, value): break if variable_exists: - renew = Command(command="renewcommand", - arguments=[NoEscape(name_arg), value]) + renew = Command( + command="renewcommand", arguments=[NoEscape(name_arg), value] + ) self.append(renew) else: - new = Command(command="newcommand", - arguments=[NoEscape(name_arg), value]) + new = Command(command="newcommand", arguments=[NoEscape(name_arg), value]) self.variables.append(new) diff --git a/pylatex/figure.py b/pylatex/figure.py index efbdd979..0e3add51 100644 --- a/pylatex/figure.py +++ b/pylatex/figure.py @@ -7,18 +7,23 @@ """ import posixpath +import uuid -from .utils import fix_filename, make_temp_dir, NoEscape, escape_latex from .base_classes import Float, UnsafeCommand from .package import Package -import uuid +from .utils import NoEscape, escape_latex, fix_filename, make_temp_dir class Figure(Float): """A class that represents a Figure environment.""" - def add_image(self, filename, *, width=NoEscape(r'0.8\textwidth'), - placement=NoEscape(r'\centering')): + def add_image( + self, + filename, + *, + width=NoEscape(r"0.8\textwidth"), + placement=NoEscape(r"\centering") + ): """Add an image to the figure. Args @@ -36,15 +41,16 @@ def add_image(self, filename, *, width=NoEscape(r'0.8\textwidth'), if self.escape: width = escape_latex(width) - width = 'width=' + str(width) + width = "width=" + str(width) if placement is not None: self.append(placement) - self.append(StandAloneGraphic(image_options=width, - filename=fix_filename(filename))) + self.append( + StandAloneGraphic(image_options=width, filename=fix_filename(filename)) + ) - def _save_plot(self, *args, extension='pdf', **kwargs): + def _save_plot(self, *args, extension="pdf", **kwargs): """Save the plot. Returns @@ -55,13 +61,13 @@ def _save_plot(self, *args, extension='pdf', **kwargs): import matplotlib.pyplot as plt tmp_path = make_temp_dir() - filename = '{}.{}'.format(str(uuid.uuid4()), extension.strip('.')) + filename = "{}.{}".format(str(uuid.uuid4()), extension.strip(".")) filepath = posixpath.join(tmp_path, filename) plt.savefig(filepath, *args, **kwargs) return filepath - def add_plot(self, *args, extension='pdf', **kwargs): + def add_plot(self, *args, extension="pdf", **kwargs): """Add the current Matplotlib plot to the figure. The plot that gets added is the one that would normally be shown when @@ -82,7 +88,7 @@ def add_plot(self, *args, extension='pdf', **kwargs): add_image_kwargs = {} - for key in ('width', 'placement'): + for key in ("width", "placement"): if key in kwargs: add_image_kwargs[key] = kwargs.pop(key) @@ -94,17 +100,17 @@ def add_plot(self, *args, extension='pdf', **kwargs): class SubFigure(Figure): """A class that represents a subfigure from the subcaption package.""" - packages = [Package('subcaption')] + packages = [Package("subcaption")] #: By default a subfigure is not on its own paragraph since that looks #: weird inside another figure. separate_paragraph = False _repr_attributes_mapping = { - 'width': 'arguments', + "width": "arguments", } - def __init__(self, width=NoEscape(r'0.45\linewidth'), **kwargs): + def __init__(self, width=NoEscape(r"0.45\linewidth"), **kwargs): """ Args ---- @@ -116,8 +122,7 @@ def __init__(self, width=NoEscape(r'0.45\linewidth'), **kwargs): super().__init__(arguments=width, **kwargs) - def add_image(self, filename, *, width=NoEscape(r'\linewidth'), - placement=None): + def add_image(self, filename, *, width=NoEscape(r"\linewidth"), placement=None): """Add an image to the subfigure. Args @@ -138,16 +143,16 @@ class StandAloneGraphic(UnsafeCommand): _latex_name = "includegraphics" - packages = [Package('graphicx')] + packages = [Package("graphicx")] - _repr_attributes_mapping = { - "filename": "arguments", - "image_options": "options" - } + _repr_attributes_mapping = {"filename": "arguments", "image_options": "options"} - def __init__(self, filename, - image_options=NoEscape(r'width=0.8\textwidth'), - extra_arguments=None): + def __init__( + self, + filename, + image_options=NoEscape(r"width=0.8\textwidth"), + extra_arguments=None, + ): r""" Args ---- @@ -159,6 +164,9 @@ def __init__(self, filename, arguments = [NoEscape(filename)] - super().__init__(command=self._latex_name, arguments=arguments, - options=image_options, - extra_arguments=extra_arguments) + super().__init__( + command=self._latex_name, + arguments=arguments, + options=image_options, + extra_arguments=extra_arguments, + ) diff --git a/pylatex/frames.py b/pylatex/frames.py index 30b7d671..39f9b978 100644 --- a/pylatex/frames.py +++ b/pylatex/frames.py @@ -6,14 +6,14 @@ :license: MIT, see License for more details. """ -from .base_classes import Environment, ContainerCommand +from .base_classes import ContainerCommand, Environment from .package import Package class MdFramed(Environment): """A class that defines an mdframed environment.""" - packages = [Package('mdframed')] + packages = [Package("mdframed")] class FBox(ContainerCommand): diff --git a/pylatex/headfoot.py b/pylatex/headfoot.py index 2cc236b2..b6d6539e 100644 --- a/pylatex/headfoot.py +++ b/pylatex/headfoot.py @@ -6,7 +6,7 @@ :license: MIT, see License for more details. """ -from .base_classes import ContainerCommand, Command +from .base_classes import Command, ContainerCommand from .package import Package from .utils import NoEscape @@ -16,10 +16,9 @@ class PageStyle(ContainerCommand): _latex_name = "fancypagestyle" - packages = [Package('fancyhdr')] + packages = [Package("fancyhdr")] - def __init__(self, name, *, header_thickness=0, footer_thickness=0, - data=None): + def __init__(self, name, *, header_thickness=0, footer_thickness=0, data=None): r""" Args ---- @@ -59,12 +58,19 @@ def change_thickness(self, element, thickness): """ if element == "header": - self.data.append(Command("renewcommand", - arguments=[NoEscape(r"\headrulewidth"), - str(thickness) + 'pt'])) + self.data.append( + Command( + "renewcommand", + arguments=[NoEscape(r"\headrulewidth"), str(thickness) + "pt"], + ) + ) elif element == "footer": - self.data.append(Command("renewcommand", arguments=[ - NoEscape(r"\footrulewidth"), str(thickness) + 'pt'])) + self.data.append( + Command( + "renewcommand", + arguments=[NoEscape(r"\footrulewidth"), str(thickness) + "pt"], + ) + ) def simple_page_number(): @@ -76,7 +82,7 @@ def simple_page_number(): The latex string that displays the page number """ - return NoEscape(r'Page \thepage\ of \pageref{LastPage}') + return NoEscape(r"Page \thepage\ of \pageref{LastPage}") class Head(ContainerCommand): diff --git a/pylatex/labelref.py b/pylatex/labelref.py index 099c6a86..bb5ec1e4 100644 --- a/pylatex/labelref.py +++ b/pylatex/labelref.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- """This module implements the label command and reference.""" -from .base_classes import CommandBase +from .base_classes import CommandBase, LatexObject from .package import Package -from .base_classes import LatexObject def _remove_invalid_char(s): """Remove invalid and dangerous characters from a string.""" - s = ''.join([i if ord(i) >= 32 and ord(i) < 127 else '' for i in s]) + s = "".join([i if ord(i) >= 32 and ord(i) < 127 else "" for i in s]) s = s.translate(dict.fromkeys(map(ord, "&%$#_{}~^\\\n\xA0[]\":;' "))) return s @@ -18,8 +17,8 @@ class Marker(LatexObject): """A class that represents a marker (label/ref parameter).""" _repr_attributes_override = [ - 'name', - 'prefix', + "name", + "prefix", ] def __init__(self, name, prefix="", del_invalid_char=True): @@ -59,7 +58,7 @@ class RefLabelBase(CommandBase): """A class used as base for command that take a marker only.""" _repr_attributes_mapping = { - 'marker': 'arguments', + "marker": "arguments", } def __init__(self, marker): @@ -89,37 +88,37 @@ class Pageref(RefLabelBase): class Eqref(RefLabelBase): """A class that represent a ref to a formulae.""" - packages = [Package('amsmath')] + packages = [Package("amsmath")] class Cref(RefLabelBase): """A class that represent a cref (not a Cref).""" - packages = [Package('cleveref')] + packages = [Package("cleveref")] class CrefUp(RefLabelBase): """A class that represent a Cref.""" - packages = [Package('cleveref')] - latex_name = 'Cref' + packages = [Package("cleveref")] + latex_name = "Cref" class Autoref(RefLabelBase): """A class that represent an autoref.""" - packages = [Package('hyperref')] + packages = [Package("hyperref")] class Hyperref(CommandBase): """A class that represents an hyperlink to a label.""" _repr_attributes_mapping = { - 'marker': 'options', - 'text': 'arguments', + "marker": "options", + "text": "arguments", } - packages = [Package('hyperref')] + packages = [Package("hyperref")] def __init__(self, marker, text): """ diff --git a/pylatex/lists.py b/pylatex/lists.py index 87ded8e4..a15d4a57 100644 --- a/pylatex/lists.py +++ b/pylatex/lists.py @@ -8,10 +8,11 @@ :license: MIT, see License for more details. """ -from .base_classes import Environment, Command, Options -from .package import Package from pylatex.utils import NoEscape +from .base_classes import Command, Environment, Options +from .package import Package + class List(Environment): """A base class that represents a list.""" @@ -28,7 +29,7 @@ def add_item(self, s): s: str or `~.LatexObject` The item itself. """ - self.append(Command('item')) + self.append(Command("item")) self.append(s) @@ -58,8 +59,7 @@ def __init__(self, enumeration_symbol=None, *, options=None, **kwargs): options = Options(options) else: options = Options() - options._positional_args.append(NoEscape('label=' + - enumeration_symbol)) + options._positional_args.append(NoEscape("label=" + enumeration_symbol)) super().__init__(options=options, **kwargs) @@ -81,5 +81,5 @@ def add_item(self, label, s): s: str or `~.LatexObject` The item itself. """ - self.append(Command('item', options=label)) + self.append(Command("item", options=label)) self.append(s) diff --git a/pylatex/math.py b/pylatex/math.py index ebe52696..19a1b9b2 100644 --- a/pylatex/math.py +++ b/pylatex/math.py @@ -16,7 +16,7 @@ class Alignat(Environment): #: Alignat environment cause compile errors when they do not contain items. #: This is why it is omitted fully if they are empty. omit_if_empty = True - packages = [Package('amsmath')] + packages = [Package("amsmath")] def __init__(self, aligns=2, numbering=True, escape=None): """ @@ -40,9 +40,9 @@ def __init__(self, aligns=2, numbering=True, escape=None): class Math(Container): """A class representing a math environment.""" - packages = [Package('amsmath')] + packages = [Package("amsmath")] - content_separator = ' ' + content_separator = " " def __init__(self, *, inline=False, data=None, escape=None): r""" @@ -69,15 +69,15 @@ def dumps(self): """ if self.inline: - return '$' + self.dumps_content() + '$' - return '\\[%\n' + self.dumps_content() + '%\n\\]' + return "$" + self.dumps_content() + "$" + return "\\[%\n" + self.dumps_content() + "%\n\\]" class VectorName(Command): """A class representing a named vector.""" _repr_attributes_mapping = { - 'name': 'arguments', + "name": "arguments", } def __init__(self, name): @@ -88,19 +88,19 @@ def __init__(self, name): Name of the vector """ - super().__init__('mathbf', arguments=name) + super().__init__("mathbf", arguments=name) class Matrix(Environment): """A class representing a matrix.""" - packages = [Package('amsmath')] + packages = [Package("amsmath")] _repr_attributes_mapping = { - 'alignment': 'arguments', + "alignment": "arguments", } - def __init__(self, matrix, *, mtype='p', alignment=None): + def __init__(self, matrix, *, mtype="p", alignment=None): r""" Args ---- @@ -123,10 +123,10 @@ def __init__(self, matrix, *, mtype='p', alignment=None): self.matrix = matrix - self.latex_name = mtype + 'matrix' + self.latex_name = mtype + "matrix" self._mtype = mtype if alignment is not None: - self.latex_name += '*' + self.latex_name += "*" super().__init__(arguments=alignment) @@ -140,16 +140,16 @@ def dumps_content(self): import numpy as np - string = '' + string = "" shape = self.matrix.shape for (y, x), value in np.ndenumerate(self.matrix): if x: - string += '&' + string += "&" string += str(value) if x == shape[1] - 1 and y != shape[0] - 1: - string += r'\\' + '%\n' + string += r"\\" + "%\n" super().dumps_content() diff --git a/pylatex/package.py b/pylatex/package.py index 040bed53..ccbbf30f 100644 --- a/pylatex/package.py +++ b/pylatex/package.py @@ -12,10 +12,10 @@ class Package(CommandBase): """A class that represents a package.""" - _latex_name = 'usepackage' + _latex_name = "usepackage" _repr_attributes_mapping = { - 'name': 'arguments', + "name": "arguments", } def __init__(self, name, options=None): diff --git a/pylatex/position.py b/pylatex/position.py index 396c0f15..9073a0f5 100644 --- a/pylatex/position.py +++ b/pylatex/position.py @@ -8,7 +8,7 @@ :license: MIT, see License for more details. """ -from .base_classes import Environment, SpecialOptions, Command, CommandBase +from .base_classes import Command, CommandBase, Environment, SpecialOptions from .package import Package from .utils import NoEscape @@ -16,11 +16,9 @@ class HorizontalSpace(CommandBase): """Add/remove the amount of horizontal space between elements.""" - _latex_name = 'hspace' + _latex_name = "hspace" - _repr_attributes_mapping = { - "size": "arguments" - } + _repr_attributes_mapping = {"size": "arguments"} def __init__(self, size, *, star=True): """ @@ -34,7 +32,7 @@ def __init__(self, size, *, star=True): """ if star: - self.latex_name += '*' + self.latex_name += "*" super().__init__(arguments=size) @@ -42,13 +40,13 @@ def __init__(self, size, *, star=True): class VerticalSpace(HorizontalSpace): """Add the user specified amount of vertical space to the document.""" - _latex_name = 'vspace' + _latex_name = "vspace" class Center(Environment): r"""Centered environment.""" - packages = [Package('ragged2e')] + packages = [Package("ragged2e")] class FlushLeft(Center): @@ -62,19 +60,27 @@ class FlushRight(Center): class MiniPage(Environment): r"""A class that allows the creation of minipages within document pages.""" - packages = [Package('ragged2e')] + packages = [Package("ragged2e")] _repr_attributes_mapping = { "width": "arguments", "pos": "options", "height": "options", "content_pos": "options", - "align": "options" + "align": "options", } - def __init__(self, *, width=NoEscape(r'\textwidth'), pos=None, - height=None, content_pos=None, align=None, fontsize=None, - data=None): + def __init__( + self, + *, + width=NoEscape(r"\textwidth"), + pos=None, + height=None, + content_pos=None, + align=None, + fontsize=None, + data=None + ): r""" Args ---- @@ -104,8 +110,7 @@ def __init__(self, *, width=NoEscape(r'\textwidth'), pos=None, if height is not None: options.append(NoEscape(height)) - if ((content_pos is not None) and (pos is not None) and - (height is not None)): + if (content_pos is not None) and (pos is not None) and (height is not None): options.append(content_pos) options = SpecialOptions(*options) @@ -142,14 +147,11 @@ class TextBlock(Environment): Make sure to set lengths of TPHorizModule and TPVertModule """ - _repr_attributes_mapping = { - "width": "arguments" - } + _repr_attributes_mapping = {"width": "arguments"} - packages = [Package('textpos')] + packages = [Package("textpos")] - def __init__(self, width, horizontal_pos, vertical_pos, *, - indent=False, data=None): + def __init__(self, width, horizontal_pos, vertical_pos, *, indent=False, data=None): r""" Args ---- @@ -171,8 +173,7 @@ def __init__(self, width, horizontal_pos, vertical_pos, *, super().__init__(arguments=arguments) - self.append("(%s, %s)" % (str(self.horizontal_pos), - str(self.vertical_pos))) + self.append("(%s, %s)" % (str(self.horizontal_pos), str(self.vertical_pos))) if not indent: - self.append(NoEscape(r'\noindent')) + self.append(NoEscape(r"\noindent")) diff --git a/pylatex/quantities.py b/pylatex/quantities.py index d6e60c8a..8780433c 100644 --- a/pylatex/quantities.py +++ b/pylatex/quantities.py @@ -18,34 +18,33 @@ from .package import Package from .utils import NoEscape, escape_latex - # Translations for names used in the quantities package to ones used by SIunitx UNIT_NAME_TRANSLATIONS = { - 'Celsius': 'celsius', - 'revolutions_per_minute': 'rpm', - 'v': 'volt', + "Celsius": "celsius", + "revolutions_per_minute": "rpm", + "v": "volt", } def _dimensionality_to_siunitx(dim): import quantities as pq - string = '' + string = "" items = dim.items() for unit, power in sorted(items, key=itemgetter(1), reverse=True): if power < 0: - substring = r'\per' + substring = r"\per" power = -power elif power == 0: continue else: - substring = '' + substring = "" - prefixes = [x for x in dir(pq.prefixes) if not x.startswith('_')] + prefixes = [x for x in dir(pq.prefixes) if not x.startswith("_")] for prefix in prefixes: # Split unitname into prefix and actual name if possible if unit.name.startswith(prefix): - substring += '\\' + prefix + substring += "\\" + prefix name = unit.name[len(prefix)] break else: @@ -58,10 +57,10 @@ def _dimensionality_to_siunitx(dim): except KeyError: pass - substring += '\\' + name + substring += "\\" + name if power > 1: - substring += r'\tothe{' + str(power) + '}' + substring += r"\tothe{" + str(power) + "}" string += substring return NoEscape(string) @@ -70,8 +69,8 @@ class Quantity(Command): """A class representing quantities.""" packages = [ - Package('siunitx', options=[NoEscape('separate-uncertainty=true')]), - NoEscape('\\DeclareSIUnit\\rpm{rpm}') + Package("siunitx", options=[NoEscape("separate-uncertainty=true")]), + NoEscape("\\DeclareSIUnit\\rpm{rpm}"), ] def __init__(self, quantity, *, options=None, format_cb=None): @@ -124,19 +123,21 @@ def _format(val): return format_cb(val) if isinstance(quantity, pq.UncertainQuantity): - magnitude_str = '{} +- {}'.format( - _format(quantity.magnitude), - _format(quantity.uncertainty.magnitude)) + magnitude_str = "{} +- {}".format( + _format(quantity.magnitude), _format(quantity.uncertainty.magnitude) + ) elif isinstance(quantity, pq.Quantity): magnitude_str = _format(quantity.magnitude) if isinstance(quantity, (pq.UncertainQuantity, pq.Quantity)): unit_str = _dimensionality_to_siunitx(quantity.dimensionality) - super().__init__(command='SI', arguments=(magnitude_str, unit_str), - options=options) + super().__init__( + command="SI", arguments=(magnitude_str, unit_str), options=options + ) else: - super().__init__(command='num', arguments=_format(quantity), - options=options) + super().__init__( + command="num", arguments=_format(quantity), options=options + ) self.arguments._escape = False # dash in e.g. \num{3 +- 2} if self.options is not None: diff --git a/pylatex/section.py b/pylatex/section.py index 45745f55..493887a5 100644 --- a/pylatex/section.py +++ b/pylatex/section.py @@ -7,8 +7,8 @@ """ -from .base_classes import Container, Command -from .labelref import Marker, Label +from .base_classes import Command, Container +from .labelref import Label, Marker class Section(Container): @@ -45,8 +45,8 @@ def __init__(self, title, numbering=None, *, label=True, **kwargs): if isinstance(label, Label): self.label = label elif isinstance(label, str): - if ':' in label: - label = label.split(':', 1) + if ":" in label: + label = label.split(":", 1) self.label = Label(Marker(label[1], label[0])) else: self.label = Label(Marker(label, self.marker_prefix)) @@ -67,14 +67,14 @@ def dumps(self): """ if not self.numbering: - num = '*' + num = "*" else: - num = '' + num = "" string = Command(self.latex_name + num, self.title).dumps() if self.label is not None: - string += '%\n' + self.label.dumps() - string += '%\n' + self.dumps_content() + string += "%\n" + self.label.dumps() + string += "%\n" + self.dumps_content() return string diff --git a/pylatex/table.py b/pylatex/table.py index 255f5231..2c898cc0 100644 --- a/pylatex/table.py +++ b/pylatex/table.py @@ -6,19 +6,25 @@ :license: MIT, see License for more details. """ -from .base_classes import LatexObject, Container, Command, UnsafeCommand, \ - Float, Environment -from .package import Package -from .errors import TableRowSizeError, TableError -from .utils import dumps_list, NoEscape, _is_iterable -import pylatex.config as cf - -from collections import Counter import re +from collections import Counter +import pylatex.config as cf + +from .base_classes import ( + Command, + Container, + Environment, + Float, + LatexObject, + UnsafeCommand, +) +from .errors import TableError, TableRowSizeError +from .package import Package +from .utils import NoEscape, _is_iterable, dumps_list # The letters used to count the table width -COLUMN_LETTERS = {'l', 'c', 'r', 'p', 'm', 'b', 'X'} +COLUMN_LETTERS = {"l", "c", "r", "p", "m", "b", "X"} def _get_table_width(table_spec): @@ -37,10 +43,10 @@ def _get_table_width(table_spec): """ # Remove things like {\bfseries} - cleaner_spec = re.sub(r'{[^}]*}', '', table_spec) + cleaner_spec = re.sub(r"{[^}]*}", "", table_spec) # Remove X[] in tabu environments so they dont interfere with column count - cleaner_spec = re.sub(r'X\[(.*?(.))\]', r'\2', cleaner_spec) + cleaner_spec = re.sub(r"X\[(.*?(.))\]", r"\2", cleaner_spec) spec_counter = Counter(cleaner_spec) return sum(spec_counter[l] for l in COLUMN_LETTERS) @@ -50,13 +56,22 @@ class Tabular(Environment): """A class that represents a tabular.""" _repr_attributes_mapping = { - 'table_spec': 'arguments', - 'pos': 'options', + "table_spec": "arguments", + "pos": "options", } - def __init__(self, table_spec, data=None, pos=None, *, - row_height=None, col_space=None, width=None, booktabs=None, - **kwargs): + def __init__( + self, + table_spec, + data=None, + pos=None, + *, + row_height=None, + col_space=None, + width=None, + booktabs=None, + **kwargs + ): """ Args ---- @@ -95,16 +110,15 @@ def __init__(self, table_spec, data=None, pos=None, *, self.booktabs = booktabs if self.booktabs: - self.packages.add(Package('booktabs')) - table_spec = '@{}%s@{}' % table_spec + self.packages.add(Package("booktabs")) + table_spec = "@{}%s@{}" % table_spec - self.row_height = row_height if row_height is not None else \ - cf.active.row_height + self.row_height = row_height if row_height is not None else cf.active.row_height self.col_space = col_space - super().__init__(data=data, options=pos, - arguments=NoEscape(table_spec), - **kwargs) + super().__init__( + data=data, options=pos, arguments=NoEscape(table_spec), **kwargs + ) # Parameter that determines if the xcolor package has been added. self.color = False @@ -115,16 +129,16 @@ def dumps(self): string = "" if self.row_height is not None: - row_height = Command('renewcommand', arguments=[ - NoEscape(r'\arraystretch'), - self.row_height]) - string += row_height.dumps() + '%\n' + row_height = Command( + "renewcommand", arguments=[NoEscape(r"\arraystretch"), self.row_height] + ) + string += row_height.dumps() + "%\n" if self.col_space is not None: - col_space = Command('setlength', arguments=[ - NoEscape(r'\tabcolsep'), - self.col_space]) - string += col_space.dumps() + '%\n' + col_space = Command( + "setlength", arguments=[NoEscape(r"\tabcolsep"), self.col_space] + ) + string += col_space.dumps() + "%\n" return string + super().dumps() @@ -144,19 +158,18 @@ def dumps_content(self, **kwargs): A LaTeX string representing the """ - content = '' + content = "" if self.booktabs: - content += '\\toprule%\n' + content += "\\toprule%\n" content += super().dumps_content(**kwargs) if self.booktabs: - content += '\\bottomrule%\n' + content += "\\bottomrule%\n" return NoEscape(content) - def add_hline(self, start=None, end=None, *, color=None, - cmidruleoption=None): + def add_hline(self, start=None, end=None, *, color=None, cmidruleoption=None): r"""Add a horizontal line to the table. Args @@ -172,17 +185,17 @@ def add_hline(self, start=None, end=None, *, color=None, ``\cmidrule(x){1-3}``. """ if self.booktabs: - hline = 'midrule' - cline = 'cmidrule' + hline = "midrule" + cline = "cmidrule" if cmidruleoption is not None: - cline += '(' + cmidruleoption + ')' + cline += "(" + cmidruleoption + ")" else: - hline = 'hline' - cline = 'cline' + hline = "hline" + cline = "cline" if color is not None: if not self.color: - self.packages.append(Package('xcolor', options='table')) + self.packages.append(Package("xcolor", options="table")) self.color = True color_command = Command(command="arrayrulecolor", arguments=color) self.append(color_command) @@ -195,16 +208,14 @@ def add_hline(self, start=None, end=None, *, color=None, elif end is None: end = self.width - self.append(Command(cline, - dumps_list([start, NoEscape('-'), end]))) + self.append(Command(cline, dumps_list([start, NoEscape("-"), end]))) def add_empty_row(self): """Add an empty row to the table.""" - self.append(NoEscape((self.width - 1) * '&' + r'\\')) + self.append(NoEscape((self.width - 1) * "&" + r"\\")) - def add_row(self, *cells, color=None, escape=None, mapper=None, - strict=True): + def add_row(self, *cells, color=None, escape=None, mapper=None, strict=True): """Add a row of cells to the table. Args @@ -246,28 +257,30 @@ def add_row(self, *cells, color=None, escape=None, mapper=None, cell_count += 1 if strict and cell_count != self.width: - msg = "Number of cells added to table ({}) " \ + msg = ( + "Number of cells added to table ({}) " "did not match table width ({})".format(cell_count, self.width) + ) raise TableRowSizeError(msg) if color is not None: if not self.color: - self.packages.append(Package("xcolor", options='table')) + self.packages.append(Package("xcolor", options="table")) self.color = True color_command = Command(command="rowcolor", arguments=color) self.append(color_command) - self.append(dumps_list(cells, escape=escape, token='&', - mapper=mapper) + NoEscape(r'\\')) + self.append( + dumps_list(cells, escape=escape, token="&", mapper=mapper) + NoEscape(r"\\") + ) class Tabularx(Tabular): """A class that represents a tabularx environment.""" - packages = [Package('tabularx')] + packages = [Package("tabularx")] - def __init__(self, *args, width_argument=NoEscape(r'\textwidth'), - **kwargs): + def __init__(self, *args, width_argument=NoEscape(r"\textwidth"), **kwargs): """ Args ---- @@ -283,7 +296,7 @@ class MultiColumn(Container): # TODO: Make this subclass of ContainerCommand - def __init__(self, size, *, align='c', color=None, data=None): + def __init__(self, size, *, align="c", color=None, data=None): """ Args ---- @@ -304,7 +317,7 @@ def __init__(self, size, *, align='c', color=None, data=None): # Add a cell color to the MultiColumn if color is not None: - self.packages.append(Package('xcolor', options='table')) + self.packages.append(Package("xcolor", options="table")) color_command = Command("cellcolor", arguments=color) self.append(color_command) @@ -327,9 +340,9 @@ class MultiRow(Container): # TODO: Make this subclass CommandBase and Container - packages = [Package('multirow')] + packages = [Package("multirow")] - def __init__(self, size, *, width='*', color=None, data=None): + def __init__(self, size, *, width="*", color=None, data=None): """ Args ---- @@ -350,7 +363,7 @@ def __init__(self, size, *, width='*', color=None, data=None): super().__init__(data=data) if color is not None: - self.packages.append(Package('xcolor', options='table')) + self.packages.append(Package("xcolor", options="table")) color_command = Command("cellcolor", arguments=color) self.append(color_command) @@ -375,11 +388,22 @@ class Table(Float): class Tabu(Tabular): """A class that represents a tabu (more flexible table).""" - packages = [Package('tabu')] - - def __init__(self, table_spec, data=None, pos=None, *, - row_height=None, col_space=None, width=None, booktabs=None, - spread=None, to=None, **kwargs): + packages = [Package("tabu")] + + def __init__( + self, + table_spec, + data=None, + pos=None, + *, + row_height=None, + col_space=None, + width=None, + booktabs=None, + spread=None, + to=None, + **kwargs + ): """ Args ---- @@ -415,9 +439,16 @@ def __init__(self, table_spec, data=None, pos=None, *, * https://en.wikibooks.org/wiki/LaTeX/Tables#The_tabular_environment """ - super().__init__(table_spec, data, pos, - row_height=row_height, col_space=col_space, - width=width, booktabs=booktabs, **kwargs) + super().__init__( + table_spec, + data, + pos, + row_height=row_height, + col_space=col_space, + width=width, + booktabs=booktabs, + **kwargs + ) self._preamble = "" if spread: @@ -442,8 +473,10 @@ def dumps(self): elif _s.startswith(r"\begin{tabu}"): _s = _s[:12] + self._preamble + _s[12:] else: - raise TableError("Can't apply preamble to Tabu table " - "(unexpected initial command sequence)") + raise TableError( + "Can't apply preamble to Tabu table " + "(unexpected initial command sequence)" + ) return _s @@ -451,7 +484,7 @@ def dumps(self): class LongTable(Tabular): """A class that represents a longtable (multipage table).""" - packages = [Package('longtable')] + packages = [Package("longtable")] header = False foot = False @@ -466,7 +499,7 @@ def end_table_header(self): self.header = True - self.append(Command(r'endhead')) + self.append(Command(r"endhead")) def end_table_footer(self): r"""End the table foot which will appear on every page.""" @@ -477,7 +510,7 @@ def end_table_footer(self): self.foot = True - self.append(Command('endfoot')) + self.append(Command("endfoot")) def end_table_last_footer(self): r"""End the table foot which will appear on the last page.""" @@ -488,7 +521,7 @@ def end_table_last_footer(self): self.lastFoot = True - self.append(Command('endlastfoot')) + self.append(Command("endlastfoot")) class LongTabu(LongTable, Tabu): @@ -504,9 +537,9 @@ class LongTabularx(Tabularx, LongTable): elements in that document over multiple pages as well. """ - _latex_name = 'tabularx' + _latex_name = "tabularx" - packages = [Package('ltablex')] + packages = [Package("ltablex")] class ColumnType(UnsafeCommand): @@ -517,10 +550,7 @@ class ColumnType(UnsafeCommand): questions/257128/how-does-the-newcolumntype-command-work>`_. """ - _repr_attributes_mapping = { - 'name': 'arguments', - 'parameters': 'options' - } + _repr_attributes_mapping = {"name": "arguments", "parameters": "options"} def __init__(self, name, base, modifications, *, parameters=None): """ @@ -546,13 +576,17 @@ def __init__(self, name, base, modifications, *, parameters=None): if parameters is None: # count the number of non escaped # parameters - parameters = len(re.findall(r'(?{%s\arraybackslash}%s" % (modifications, base) - super().__init__(command="newcolumntype", arguments=name, - options=parameters, extra_arguments=modified) + super().__init__( + command="newcolumntype", + arguments=name, + options=parameters, + extra_arguments=modified, + ) diff --git a/pylatex/tikz.py b/pylatex/tikz.py index 98add4be..0ede8197 100644 --- a/pylatex/tikz.py +++ b/pylatex/tikz.py @@ -6,10 +6,11 @@ :license: MIT, see License for more details. """ -from .base_classes import LatexObject, Environment, Command, Options, Container -from .package import Package -import re import math +import re + +from .base_classes import Command, Container, Environment, LatexObject, Options +from .package import Package class TikZOptions(Options): @@ -26,14 +27,14 @@ def append_positional(self, option): class TikZ(Environment): """Basic TikZ container class.""" - _latex_name = 'tikzpicture' - packages = [Package('tikz')] + _latex_name = "tikzpicture" + packages = [Package("tikz")] class Axis(Environment): """PGFPlots axis container class, this contains plots.""" - packages = [Package('pgfplots'), Command('pgfplotsset', 'compat=newest')] + packages = [Package("pgfplots"), Command("pgfplotsset", "compat=newest")] def __init__(self, options=None, *, data=None): """ @@ -49,14 +50,15 @@ def __init__(self, options=None, *, data=None): class TikZScope(Environment): """TikZ Scope Environment.""" - _latex_name = 'scope' + _latex_name = "scope" class TikZCoordinate(LatexObject): """A General Purpose Coordinate Class.""" - _coordinate_str_regex = re.compile(r'(\+\+)?\(\s*(-?[0-9]+(\.[0-9]+)?)\s*' - r',\s*(-?[0-9]+(\.[0-9]+)?)\s*\)') + _coordinate_str_regex = re.compile( + r"(\+\+)?\(\s*(-?[0-9]+(\.[0-9]+)?)\s*" r",\s*(-?[0-9]+(\.[0-9]+)?)\s*\)" + ) def __init__(self, x, y, relative=False): """ @@ -75,10 +77,10 @@ def __init__(self, x, y, relative=False): def __repr__(self): if self.relative: - ret_str = '++' + ret_str = "++" else: - ret_str = '' - return ret_str + '({},{})'.format(self._x, self._y) + ret_str = "" + return ret_str + "({},{})".format(self._x, self._y) def dumps(self): """Return representation.""" @@ -92,15 +94,14 @@ def from_str(cls, coordinate): m = cls._coordinate_str_regex.match(coordinate) if m is None: - raise ValueError('invalid coordinate string') + raise ValueError("invalid coordinate string") - if m.group(1) == '++': + if m.group(1) == "++": relative = True else: relative = False - return TikZCoordinate( - float(m.group(2)), float(m.group(4)), relative=relative) + return TikZCoordinate(float(m.group(2)), float(m.group(4)), relative=relative) def __eq__(self, other): if isinstance(other, tuple): @@ -113,47 +114,47 @@ def __eq__(self, other): other_x = other._x other_y = other._y else: - raise TypeError('can only compare tuple and TiKZCoordinate types') + raise TypeError("can only compare tuple and TiKZCoordinate types") # prevent comparison between relative and non relative # by returning False - if (other_relative != self.relative): + if other_relative != self.relative: return False # return comparison result - return (other_x == self._x and other_y == self._y) + return other_x == self._x and other_y == self._y def _arith_check(self, other): if isinstance(other, tuple): other_coord = TikZCoordinate(*other) elif isinstance(other, TikZCoordinate): if other.relative is True or self.relative is True: - raise ValueError('refusing to add relative coordinates') + raise ValueError("refusing to add relative coordinates") other_coord = other else: - raise TypeError('can only add tuple or TiKZCoordinate types') + raise TypeError("can only add tuple or TiKZCoordinate types") return other_coord def __add__(self, other): other_coord = self._arith_check(other) - return TikZCoordinate(self._x + other_coord._x, - self._y + other_coord._y) + return TikZCoordinate(self._x + other_coord._x, self._y + other_coord._y) def __radd__(self, other): self.__add__(other) def __sub__(self, other): other_coord = self._arith_check(other) - return TikZCoordinate(self._x - other_coord._y, - self._y - other_coord._y) + return TikZCoordinate(self._x - other_coord._y, self._y - other_coord._y) def distance_to(self, other): """Euclidean distance between two coordinates.""" other_coord = self._arith_check(other) - return math.sqrt(math.pow(self._x - other_coord._x, 2) + - math.pow(self._y - other_coord._y, 2)) + return math.sqrt( + math.pow(self._x - other_coord._x, 2) + + math.pow(self._y - other_coord._y, 2) + ) class TikZObject(Container): @@ -188,7 +189,7 @@ def __init__(self, node_handle, anchor_name): self.anchor = anchor_name def __repr__(self): - return '({}.{})'.format(self.handle, self.anchor) + return "({}.{})".format(self.handle, self.anchor) def dumps(self): """Return a representation. Alias for consistency.""" @@ -199,7 +200,7 @@ def dumps(self): class TikZNode(TikZObject): """A class that represents a TiKZ node.""" - _possible_anchors = ['north', 'south', 'east', 'west'] + _possible_anchors = ["north", "south", "east", "west"] def __init__(self, handle=None, options=None, at=None, text=None): """ @@ -222,8 +223,8 @@ def __init__(self, handle=None, options=None, at=None, text=None): self._node_position = at else: raise TypeError( - 'at parameter must be an object of the' - 'TikzCoordinate class') + "at parameter must be an object of the" "TikzCoordinate class" + ) self._node_text = text @@ -231,20 +232,20 @@ def dumps(self): """Return string representation of the node.""" ret_str = [] - ret_str.append(Command('node', options=self.options).dumps()) + ret_str.append(Command("node", options=self.options).dumps()) if self.handle is not None: - ret_str.append('({})'.format(self.handle)) + ret_str.append("({})".format(self.handle)) if self._node_position is not None: - ret_str.append('at {}'.format(str(self._node_position))) + ret_str.append("at {}".format(str(self._node_position))) if self._node_text is not None: - ret_str.append('{{{text}}};'.format(text=self._node_text)) + ret_str.append("{{{text}}};".format(text=self._node_text)) else: - ret_str.append('{};') + ret_str.append("{};") - return ' '.join(ret_str) + return " ".join(ret_str) def get_anchor_point(self, anchor_name): """Return an anchor point of the node, if it exists.""" @@ -253,7 +254,7 @@ def get_anchor_point(self, anchor_name): return TikZNodeAnchor(self.handle, anchor_name) else: try: - anchor = int(anchor_name.split('_')[1]) + anchor = int(anchor_name.split("_")[1]) except: anchor = None @@ -303,9 +304,7 @@ def dumps(self): class TikZPathList(LatexObject): """Represents a path drawing.""" - _legal_path_types = ['--', '-|', '|-', 'to', - 'rectangle', 'circle', - 'arc', 'edge'] + _legal_path_types = ["--", "-|", "|-", "to", "rectangle", "circle", "arc", "edge"] def __init__(self, *args): """ @@ -325,7 +324,6 @@ def append(self, item): self._parse_next_item(item) def _parse_next_item(self, item): - # assume first item is a point if self._last_item_type is None: try: @@ -333,10 +331,10 @@ def _parse_next_item(self, item): except (TypeError, ValueError): # not a point, do something raise TypeError( - 'First element of path list must be a node identifier' - ' or coordinate' + "First element of path list must be a node identifier" + " or coordinate" ) - elif self._last_item_type == 'point': + elif self._last_item_type == "point": # point after point is permitted, doesnt draw try: self._add_point(item) @@ -347,7 +345,7 @@ def _parse_next_item(self, item): # will raise typeerror if wrong self._add_path(item) - elif self._last_item_type == 'path': + elif self._last_item_type == "path": # only point allowed after path original_exception = None try: @@ -366,14 +364,14 @@ def _parse_next_item(self, item): # disentangle exceptions if not_a_path is False: - raise ValueError('only a point descriptor can come' - ' after a path descriptor') + raise ValueError( + "only a point descriptor can come" " after a path descriptor" + ) if original_exception is not None: raise original_exception def _parse_arg_list(self, args): - for item in args: self._parse_next_item(item) @@ -386,12 +384,12 @@ def _add_path(self, path, parse_only=False): elif isinstance(path, TikZUserPath): _path = path else: - raise TypeError('Only string or TikZUserPath types are allowed') + raise TypeError("Only string or TikZUserPath types are allowed") # add if parse_only is False: self._arg_list.append(_path) - self._last_item_type = 'path' + self._last_item_type = "path" else: return _path @@ -406,17 +404,19 @@ def _add_point(self, point, parse_only=False): elif isinstance(point, tuple): _item = TikZCoordinate(*point) elif isinstance(point, TikZNode): - _item = '({})'.format(point.handle) + _item = "({})".format(point.handle) elif isinstance(point, TikZNodeAnchor): _item = point.dumps() else: - raise TypeError('Only str, tuple, TikZCoordinate,' - 'TikZNode or TikZNodeAnchor types are allowed,' - ' got: {}'.format(type(point))) + raise TypeError( + "Only str, tuple, TikZCoordinate," + "TikZNode or TikZNodeAnchor types are allowed," + " got: {}".format(type(point)) + ) # add, finally if parse_only is False: self._arg_list.append(_item) - self._last_item_type = 'point' + self._last_item_type = "point" else: return _item @@ -432,7 +432,7 @@ def dumps(self): elif isinstance(item, str): ret_str.append(item) - return ' '.join(ret_str) + return " ".join(ret_str) class TikZPath(TikZObject): @@ -457,8 +457,7 @@ def __init__(self, path=None, options=None): elif path is None: self.path = TikZPathList() else: - raise TypeError( - 'argument "path" can only be of types list or TikZPathList') + raise TypeError('argument "path" can only be of types list or TikZPathList') def append(self, element): """Append a path element to the current list.""" @@ -467,11 +466,11 @@ def append(self, element): def dumps(self): """Return a representation for the command.""" - ret_str = [Command('path', options=self.options).dumps()] + ret_str = [Command("path", options=self.options).dumps()] ret_str.append(self.path.dumps()) - return ' '.join(ret_str) + ';' + return " ".join(ret_str) + ";" class TikZDraw(TikZPath): @@ -490,22 +489,19 @@ def __init__(self, path=None, options=None): # append option if self.options is not None: - self.options.append_positional('draw') + self.options.append_positional("draw") else: - self.options = TikZOptions('draw') + self.options = TikZOptions("draw") class Plot(LatexObject): """A class representing a PGFPlot.""" - packages = [Package('pgfplots'), Command('pgfplotsset', 'compat=newest')] + packages = [Package("pgfplots"), Command("pgfplotsset", "compat=newest")] - def __init__(self, - name=None, - func=None, - coordinates=None, - error_bar=None, - options=None): + def __init__( + self, name=None, func=None, coordinates=None, error_bar=None, options=None + ): """ Args ---- @@ -535,30 +531,38 @@ def dumps(self): str """ - string = Command('addplot', options=self.options).dumps() + string = Command("addplot", options=self.options).dumps() if self.coordinates is not None: - string += ' coordinates {%\n' + string += " coordinates {%\n" if self.error_bar is None: for x, y in self.coordinates: # ie: "(x,y)" - string += '(' + str(x) + ',' + str(y) + ')%\n' + string += "(" + str(x) + "," + str(y) + ")%\n" else: - for (x, y), (e_x, e_y) in zip(self.coordinates, - self.error_bar): + for (x, y), (e_x, e_y) in zip(self.coordinates, self.error_bar): # ie: "(x,y) +- (e_x,e_y)" - string += '(' + str(x) + ',' + str(y) + \ - ') +- (' + str(e_x) + ',' + str(e_y) + ')%\n' - - string += '};%\n%\n' + string += ( + "(" + + str(x) + + "," + + str(y) + + ") +- (" + + str(e_x) + + "," + + str(e_y) + + ")%\n" + ) + + string += "};%\n%\n" elif self.func is not None: - string += '{' + self.func + '};%\n%\n' + string += "{" + self.func + "};%\n%\n" if self.name is not None: - string += Command('addlegendentry', self.name).dumps() + string += Command("addlegendentry", self.name).dumps() super().dumps() diff --git a/pylatex/utils.py b/pylatex/utils.py index ed517b04..180fd5f4 100644 --- a/pylatex/utils.py +++ b/pylatex/utils.py @@ -9,31 +9,32 @@ import os.path import shutil import tempfile + import pylatex.base_classes _latex_special_chars = { - '&': r'\&', - '%': r'\%', - '$': r'\$', - '#': r'\#', - '_': r'\_', - '{': r'\{', - '}': r'\}', - '~': r'\textasciitilde{}', - '^': r'\^{}', - '\\': r'\textbackslash{}', - '\n': '\\newline%\n', - '-': r'{-}', - '\xA0': '~', # Non-breaking space - '[': r'{[}', - ']': r'{]}', + "&": r"\&", + "%": r"\%", + "$": r"\$", + "#": r"\#", + "_": r"\_", + "{": r"\{", + "}": r"\}", + "~": r"\textasciitilde{}", + "^": r"\^{}", + "\\": r"\textbackslash{}", + "\n": "\\newline%\n", + "-": r"{-}", + "\xA0": "~", # Non-breaking space + "[": r"{[}", + "]": r"{]}", } _tmp_path = None def _is_iterable(element): - return hasattr(element, '__iter__') and not isinstance(element, str) + return hasattr(element, "__iter__") and not isinstance(element, str) class NoEscape(str): @@ -51,7 +52,7 @@ class NoEscape(str): """ def __repr__(self): - return '%s(%s)' % (self.__class__.__name__, self) + return "%s(%s)" % (self.__class__.__name__, self) def __add__(self, right): s = super().__add__(right) @@ -92,7 +93,7 @@ def escape_latex(s): if isinstance(s, NoEscape): return s - return NoEscape(''.join(_latex_special_chars.get(c, c) for c in str(s))) + return NoEscape("".join(_latex_special_chars.get(c, c) for c in str(s))) def fix_filename(path): @@ -128,25 +129,25 @@ def fix_filename(path): '\detokenize{/etc/local/foo.bar.baz/foo~1/document.pdf}' """ - path_parts = path.split('/' if os.name == 'posix' else '\\') + path_parts = path.split("/" if os.name == "posix" else "\\") dir_parts = path_parts[:-1] filename = path_parts[-1] - file_parts = filename.split('.') + file_parts = filename.split(".") - if os.name == 'posix' and len(file_parts) > 2: - filename = '{' + '.'.join(file_parts[0:-1]) + '}.' + file_parts[-1] + if os.name == "posix" and len(file_parts) > 2: + filename = "{" + ".".join(file_parts[0:-1]) + "}." + file_parts[-1] dir_parts.append(filename) - fixed_path = '/'.join(dir_parts) + fixed_path = "/".join(dir_parts) - if '~' in fixed_path: - fixed_path = r'\detokenize{' + fixed_path + '}' + if "~" in fixed_path: + fixed_path = r"\detokenize{" + fixed_path + "}" return fixed_path -def dumps_list(l, *, escape=True, token='%\n', mapper=None, as_content=True): +def dumps_list(l, *, escape=True, token="%\n", mapper=None, as_content=True): r"""Try to generate a LaTeX string of a list that can contain anything. Args @@ -185,8 +186,9 @@ def dumps_list(l, *, escape=True, token='%\n', mapper=None, as_content=True): \$100\% True """ - strings = (_latex_item_to_string(i, escape=escape, as_content=as_content) - for i in l) + strings = ( + _latex_item_to_string(i, escape=escape, as_content=as_content) for i in l + ) if mapper is not None: if not isinstance(mapper, list): @@ -260,7 +262,7 @@ def bold(s, *, escape=True): if escape: s = escape_latex(s) - return NoEscape(r'\textbf{' + s + '}') + return NoEscape(r"\textbf{" + s + "}") def italic(s, *, escape=True): @@ -290,10 +292,10 @@ def italic(s, *, escape=True): if escape: s = escape_latex(s) - return NoEscape(r'\textit{' + s + '}') + return NoEscape(r"\textit{" + s + "}") -def verbatim(s, *, delimiter='|'): +def verbatim(s, *, delimiter="|"): r"""Make the string verbatim. Wraps the given string in a \verb LaTeX command. @@ -320,7 +322,7 @@ def verbatim(s, *, delimiter='|'): \verb!pi|pe! """ - return NoEscape(r'\verb' + delimiter + s + delimiter) + return NoEscape(r"\verb" + delimiter + s + delimiter) def make_temp_dir(): diff --git a/setup.py b/setup.py index a4da4a98..deddca7a 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,15 @@ try: from setuptools import setup - from setuptools.command.install import install from setuptools.command.egg_info import egg_info + from setuptools.command.install import install except ImportError: from distutils.core import setup -import sys + +import errno import os import subprocess -import errno +import sys + import versioneer cmdclass = versioneer.get_cmdclass() @@ -21,35 +23,34 @@ ) if sys.version_info[:2] <= (3, 5): - dependencies = ['ordered-set<4.0.0'] + dependencies = ["ordered-set<4.0.0"] else: - dependencies = ['ordered-set'] + dependencies = ["ordered-set"] extras = { - 'docs': ['sphinx', 'jinja2<3.0', 'MarkupSafe==2.0.1', 'alabaster<0.7.12'], - 'matrices': ['numpy'], - 'matplotlib': ['matplotlib'], - 'quantities': ['quantities', 'numpy'], - 'testing': ['pytest>=4.6', - 'coverage', 'pytest-cov'], - 'packaging': ['twine'], + "docs": ["sphinx", "jinja2<3.0", "MarkupSafe==2.0.1", "alabaster<0.7.12"], + "matrices": ["numpy"], + "matplotlib": ["matplotlib"], + "quantities": ["quantities", "numpy"], + "testing": ["pytest>=4.6", "coverage", "pytest-cov", "black", "isort"], + "packaging": ["twine"], } if sys.version_info[0] == 3: - source_dir = '.' + source_dir = "." if sys.version_info < (3, 4): - del extras['docs'] - extras['matplotlib'] = ['matplotlib<2.0.0'] - extras['matrices'] = ['numpy<1.12.0'] - extras['quantities'][1] = 'numpy<1.12.0' + del extras["docs"] + extras["matplotlib"] = ["matplotlib<2.0.0"] + extras["matrices"] = ["numpy<1.12.0"] + extras["quantities"][1] = "numpy<1.12.0" else: - source_dir = 'python2_source' - dependencies.append('future>=0.15.2') + source_dir = "python2_source" + dependencies.append("future>=0.15.2") PY2_CONVERTED = False -extras['all'] = list(set([req for reqs in extras.values() for req in reqs])) +extras["all"] = list(set([req for reqs in extras.values() for req in reqs])) # Automatically convert the source from Python 3 to Python 2 if we need to. @@ -67,66 +68,70 @@ def initialize_options(self): def convert_to_py2(): global PY2_CONVERTED - if source_dir == 'python2_source' and not PY2_CONVERTED: - pylatex_exists = os.path.exists(os.path.join(source_dir, 'pylatex')) + if source_dir == "python2_source" and not PY2_CONVERTED: + pylatex_exists = os.path.exists(os.path.join(source_dir, "pylatex")) - if '+' not in version and pylatex_exists: + if "+" not in version and pylatex_exists: # This is an official release, just use the pre existing existing # python2_source dir return try: # Check if 3to2 exists - subprocess.check_output(['3to2', '--help']) - subprocess.check_output(['pasteurize', '--help']) + subprocess.check_output(["3to2", "--help"]) + subprocess.check_output(["pasteurize", "--help"]) except OSError as e: if e.errno != errno.ENOENT: raise if not pylatex_exists: - raise ImportError('3to2 and future need to be installed ' - 'before installing when PyLaTeX for Python ' - '2.7 when it is not installed using one of ' - 'the pip releases.') + raise ImportError( + "3to2 and future need to be installed " + "before installing when PyLaTeX for Python " + "2.7 when it is not installed using one of " + "the pip releases." + ) else: - converter = os.path.dirname(os.path.realpath(__file__)) \ - + '/convert_to_py2.sh' + converter = ( + os.path.dirname(os.path.realpath(__file__)) + "/convert_to_py2.sh" + ) subprocess.check_call([converter]) PY2_CONVERTED = True -cmdclass['install'] = CustomInstall -cmdclass['egg_info'] = CustomEggInfo - -setup(name='PyLaTeX', - version=version, - author='Jelte Fennema', - author_email='pylatex@jeltef.nl', - description='A Python library for creating LaTeX files and snippets', - long_description=open('README.rst').read(), - package_dir={'': source_dir}, - packages=['pylatex', 'pylatex.base_classes'], - url='https://github.com/JelteF/PyLaTeX', - license='MIT', - install_requires=dependencies, - extras_require=extras, - cmdclass=cmdclass, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Education', - 'Intended Audience :: End Users/Desktop', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Topic :: Software Development :: Code Generators', - 'Topic :: Text Processing :: Markup :: LaTeX', - ] - ) +cmdclass["install"] = CustomInstall +cmdclass["egg_info"] = CustomEggInfo + +setup( + name="PyLaTeX", + version=version, + author="Jelte Fennema", + author_email="pylatex@jeltef.nl", + description="A Python library for creating LaTeX files and snippets", + long_description=open("README.rst").read(), + package_dir={"": source_dir}, + packages=["pylatex", "pylatex.base_classes"], + url="https://github.com/JelteF/PyLaTeX", + license="MIT", + install_requires=dependencies, + extras_require=extras, + cmdclass=cmdclass, + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: End Users/Desktop", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Topic :: Software Development :: Code Generators", + "Topic :: Text Processing :: Markup :: LaTeX", + ], +) diff --git a/testall.sh b/testall.sh index c54746e0..171e82f4 100755 --- a/testall.sh +++ b/testall.sh @@ -52,7 +52,12 @@ python_version_long=$($python --version |& sed 's|Python \(.*\)|\1|g' | head -n if [ "$python_version" = '3' ]; then # Check code guidelines echo -e '\e[32mChecking for code style errors \e[0m' - # TODO: Replace with black + if ! black --check .; then + exit 1 + fi + if ! isort --check .; then + exit 1 + fi fi diff --git a/tests/test_args.py b/tests/test_args.py index 5d9696d1..302b7c05 100755 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -8,24 +8,81 @@ changed. """ +import matplotlib import numpy as np import quantities as pq -import matplotlib -from pylatex import Document, Section, Math, Tabular, Figure, SubFigure, \ - Package, TikZ, Axis, Plot, Itemize, Enumerate, Description, MultiColumn, \ - MultiRow, Command, Matrix, VectorName, Quantity, TableRowSizeError, \ - LongTable, FlushLeft, FlushRight, Center, MiniPage, TextBlock, \ - PageStyle, Head, Foot, StandAloneGraphic, Tabularx, ColumnType, NewLine, \ - LineBreak, NewPage, HFill, HugeText, LargeText, MediumText, \ - SmallText, FootnoteText, TextColor, FBox, MdFramed, Tabu, \ - HorizontalSpace, VerticalSpace, TikZCoordinate, TikZNode, \ - TikZNodeAnchor, TikZUserPath, TikZPathList, TikZPath, TikZDraw, \ - TikZScope, TikZOptions, Hyperref, Marker -from pylatex.utils import escape_latex, fix_filename, dumps_list, bold, \ - italic, verbatim, NoEscape - -matplotlib.use('Agg') # Not to use X server. For TravisCI. +from pylatex import ( + Axis, + Center, + ColumnType, + Command, + Description, + Document, + Enumerate, + FBox, + Figure, + FlushLeft, + FlushRight, + Foot, + FootnoteText, + Head, + HFill, + HorizontalSpace, + HugeText, + Hyperref, + Itemize, + LargeText, + LineBreak, + LongTable, + Marker, + Math, + Matrix, + MdFramed, + MediumText, + MiniPage, + MultiColumn, + MultiRow, + NewLine, + NewPage, + Package, + PageStyle, + Plot, + Quantity, + Section, + SmallText, + StandAloneGraphic, + SubFigure, + TableRowSizeError, + Tabu, + Tabular, + Tabularx, + TextBlock, + TextColor, + TikZ, + TikZCoordinate, + TikZDraw, + TikZNode, + TikZNodeAnchor, + TikZOptions, + TikZPath, + TikZPathList, + TikZScope, + TikZUserPath, + VectorName, + VerticalSpace, +) +from pylatex.utils import ( + NoEscape, + bold, + dumps_list, + escape_latex, + fix_filename, + italic, + verbatim, +) + +matplotlib.use("Agg") # Not to use X server. For TravisCI. import matplotlib.pyplot as pyplot # noqa @@ -34,25 +91,25 @@ def test_document(): "includeheadfoot": True, "headheight": "12pt", "headsep": "10pt", - "landscape": True + "landscape": True, } doc = Document( - default_filepath='default_filepath', - documentclass='article', - fontenc='T1', - inputenc='utf8', + default_filepath="default_filepath", + documentclass="article", + fontenc="T1", + inputenc="utf8", lmodern=True, data=None, page_numbers=True, indent=False, document_options=["a4paper", "12pt"], - geometry_options=geometry_options + geometry_options=geometry_options, ) repr(doc) - doc.append('Some text.') + doc.append("Some text.") doc.change_page_style(style="empty") doc.change_document_style(style="plain") doc.add_color(name="lightgray", model="gray", description="0.6") @@ -61,12 +118,12 @@ def test_document(): doc.set_variable(name="myVar", value="1234") doc.change_length(parameter=r"\headheight", value="0.5in") - doc.generate_tex(filepath='') - doc.generate_pdf(filepath='', clean=True) + doc.generate_tex(filepath="") + doc.generate_pdf(filepath="", clean=True) def test_section(): - sec = Section(title='', numbering=True, data=None) + sec = Section(title="", numbering=True, data=None) repr(sec) @@ -79,21 +136,19 @@ def test_math(): math = Math(data=None, inline=False) repr(math) - vec = VectorName(name='') + vec = VectorName(name="") repr(vec) # Numpy - m = np.matrix([[2, 3, 4], - [0, 0, 1], - [0, 0, 2]]) + m = np.matrix([[2, 3, 4], [0, 0, 1], [0, 0, 2]]) - matrix = Matrix(matrix=m, mtype='p', alignment=None) + matrix = Matrix(matrix=m, mtype="p", alignment=None) repr(matrix) def test_table(): # Tabular - t = Tabular(table_spec='|c|c|', data=None, pos=None, width=2) + t = Tabular(table_spec="|c|c|", data=None, pos=None, width=2) t.add_hline(start=None, end=None) @@ -101,39 +156,37 @@ def test_table(): t.add_row(1, 2, escape=False, strict=True, mapper=[bold]) # MultiColumn/MultiRow. - t.add_row((MultiColumn(size=2, align='|c|', data='MultiColumn'),), - strict=True) + t.add_row((MultiColumn(size=2, align="|c|", data="MultiColumn"),), strict=True) # One multiRow-cell in that table would not be proper LaTeX, # so strict is set to False - t.add_row((MultiRow(size=2, width='*', data='MultiRow'),), strict=False) + t.add_row((MultiRow(size=2, width="*", data="MultiRow"),), strict=False) repr(t) # TabularX - tabularx = Tabularx(table_spec='X X X', - width_argument=NoEscape(r"\textwidth")) + tabularx = Tabularx(table_spec="X X X", width_argument=NoEscape(r"\textwidth")) tabularx.add_row(["test1", "test2", "test3"]) # Long Table - longtable = LongTable(table_spec='c c c') + longtable = LongTable(table_spec="c c c") longtable.add_row(["test", "test2", "test3"]) longtable.end_table_header() # Colored Tabu - coloredtable = Tabu(table_spec='X[c] X[c]') + coloredtable = Tabu(table_spec="X[c] X[c]") coloredtable.add_row(["test", "test2"], color="gray", mapper=bold) # Colored Tabu with 'spread' - coloredtable = Tabu(table_spec='X[c] X[c]', spread="1in") + coloredtable = Tabu(table_spec="X[c] X[c]", spread="1in") coloredtable.add_row(["test", "test2"], color="gray", mapper=bold) # Colored Tabu with 'to' - coloredtable = Tabu(table_spec='X[c] X[c]', to="5in") + coloredtable = Tabu(table_spec="X[c] X[c]", to="5in") coloredtable.add_row(["test", "test2"], color="gray", mapper=bold) # Colored Tabularx - coloredtable = Tabularx(table_spec='X[c] X[c]') + coloredtable = Tabularx(table_spec="X[c] X[c]") coloredtable.add_row(["test", "test2"], color="gray", mapper=bold) # Column @@ -142,26 +195,24 @@ def test_table(): def test_command(): - c = Command(command='documentclass', arguments=None, options=None, - packages=None) + c = Command(command="documentclass", arguments=None, options=None, packages=None) repr(c) def test_graphics(): f = Figure(data=None, position=None) - f.add_image(filename='', width=r'0.8\textwidth', placement=r'\centering') + f.add_image(filename="", width=r"0.8\textwidth", placement=r"\centering") - f.add_caption(caption='') + f.add_caption(caption="") repr(f) # Subfigure - s = SubFigure(data=None, position=None, width=r'0.45\linewidth') + s = SubFigure(data=None, position=None, width=r"0.45\linewidth") - s.add_image(filename='', width='r\linewidth', - placement=None) + s.add_image(filename="", width="r\linewidth", placement=None) - s.add_caption(caption='') + s.add_caption(caption="") repr(s) # Matplotlib @@ -172,26 +223,27 @@ def test_graphics(): pyplot.plot(x, y) - plot.add_plot(width=r'0.8\textwidth', placement=r'\centering') - plot.add_caption(caption='I am a caption.') + plot.add_plot(width=r"0.8\textwidth", placement=r"\centering") + plot.add_caption(caption="I am a caption.") repr(plot) # StandAloneGraphic stand_alone_graphic = StandAloneGraphic( - filename='', image_options=r"width=0.8\textwidth") + filename="", image_options=r"width=0.8\textwidth" + ) repr(stand_alone_graphic) def test_quantities(): # Quantities - Quantity(quantity=1*pq.kg) - q = Quantity(quantity=1*pq.kg, format_cb=lambda x: str(int(x))) + Quantity(quantity=1 * pq.kg) + q = Quantity(quantity=1 * pq.kg, format_cb=lambda x: str(int(x))) repr(q) def test_package(): # Package - p = Package(name='', options=None) + p = Package(name="", options=None) repr(p) @@ -203,8 +255,7 @@ def test_tikz(): a = Axis(data=None, options=None) repr(a) - p = Plot(name=None, func=None, coordinates=None, error_bar=None, - options=None) + p = Plot(name=None, func=None, coordinates=None, error_bar=None, options=None) repr(p) opt = TikZOptions(None) @@ -228,14 +279,11 @@ def test_tikz(): bool(c == TikZCoordinate(1, 1)) bool(TikZCoordinate(1, 1, relative=True) == (1, 1)) bool(TikZCoordinate(1, 1, relative=False) == (1, 1)) - bool(TikZCoordinate(1, 1, relative=True) == TikZCoordinate(1, - 1, - relative=False)) + bool(TikZCoordinate(1, 1, relative=True) == TikZCoordinate(1, 1, relative=False)) # test expected to fail try: - g = TikZCoordinate(0, 1, relative=True) +\ - TikZCoordinate(1, 0, relative=False) + g = TikZCoordinate(0, 1, relative=True) + TikZCoordinate(1, 0, relative=False) repr(g) raise Exception except ValueError: @@ -250,43 +298,43 @@ def test_tikz(): p = n.get_anchor_point("north") repr(p) - p = n.get_anchor_point('_180') + p = n.get_anchor_point("_180") repr(p) p = n.west repr(p) - up = TikZUserPath(path_type="edge", options=TikZOptions('bend right')) + up = TikZUserPath(path_type="edge", options=TikZOptions("bend right")) repr(up) - pl = TikZPathList('(0, 1)', '--', '(2, 0)') + pl = TikZPathList("(0, 1)", "--", "(2, 0)") pl.append((0.5, 0)) repr(pl) # generate a failure, illegal start try: - pl = TikZPathList('--', '(0, 1)') + pl = TikZPathList("--", "(0, 1)") raise Exception except TypeError: pass # fail with illegal path type try: - pl = TikZPathList('(0, 1)', 'illegal', '(0, 2)') + pl = TikZPathList("(0, 1)", "illegal", "(0, 2)") raise Exception except ValueError: pass # fail with path after path try: - pl = TikZPathList('(0, 1)', '--', '--') + pl = TikZPathList("(0, 1)", "--", "--") raise Exception except ValueError: pass # other type of failure: illegal identifier after path try: - pl = TikZPathList('(0, 1)', '--', 'illegal') + pl = TikZPathList("(0, 1)", "--", "illegal") raise Exception except (ValueError, TypeError): pass @@ -295,7 +343,7 @@ def test_tikz(): pt.append(TikZCoordinate(0, 1, relative=True)) repr(pt) - pt = TikZPath(path=[n.west, 'edge', TikZCoordinate(0, 1, relative=True)]) + pt = TikZPath(path=[n.west, "edge", TikZCoordinate(0, 1, relative=True)]) repr(pt) pt = TikZPath(path=pl, options=None) @@ -312,7 +360,7 @@ def test_lists(): itemize.append("append") repr(itemize) - enum = Enumerate(enumeration_symbol=r"\alph*)", options={'start': 172}) + enum = Enumerate(enumeration_symbol=r"\alph*)", options={"start": 172}) enum.add_item(s="item") enum.add_item(s="item2") enum.append("append") @@ -344,8 +392,7 @@ def test_headfoot(): def test_position(): - - repr(HorizontalSpace(size='20pt', star=False)) + repr(HorizontalSpace(size="20pt", star=False)) repr(VerticalSpace(size="20pt", star=True)) @@ -362,13 +409,20 @@ def test_position(): left.append("append") repr(left) - minipage = MiniPage(width=r"\textwidth", height="10pt", pos='t', - align='r', content_pos='t', fontsize="Large") + minipage = MiniPage( + width=r"\textwidth", + height="10pt", + pos="t", + align="r", + content_pos="t", + fontsize="Large", + ) minipage.append("append") repr(minipage) - textblock = TextBlock(width="200", horizontal_pos="200", - vertical_pos="200", indent=True) + textblock = TextBlock( + width="200", horizontal_pos="200", vertical_pos="200", indent=True + ) textblock.append("append") textblock.dumps() repr(textblock) @@ -429,17 +483,17 @@ def test_basic(): def test_utils(): # Utils - escape_latex(s='') + escape_latex(s="") - fix_filename(path='') + fix_filename(path="") - dumps_list(l=[], escape=False, token='\n') + dumps_list(l=[], escape=False, token="\n") - bold(s='') + bold(s="") - italic(s='') + italic(s="") - verbatim(s='', delimiter='|') + verbatim(s="", delimiter="|") def test_errors(): @@ -456,7 +510,7 @@ def test_errors(): # Positive test, expected to raise Error - t = Tabular(table_spec='|c|c|', data=None, pos=None) + t = Tabular(table_spec="|c|c|", data=None, pos=None) # TODO: this does not actually check if the error is raised try: # Wrong number of cells in table should raise an exception diff --git a/tests/test_config.py b/tests/test_config.py index 479efe97..92fb30b4 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -7,8 +7,8 @@ :license: MIT, see License for more details. """ -from pylatex import Document import pylatex.config as cf +from pylatex import Document def test(): @@ -36,5 +36,5 @@ def test(): assert not Document()._indent -if __name__ == '__main__': +if __name__ == "__main__": test() diff --git a/tests/test_environment.py b/tests/test_environment.py index 8456a7a2..a880378c 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -14,7 +14,5 @@ class AllTT(Environment): alltt = AllTT() alltt.append("This is alltt content\nIn two lines") s = alltt.dumps() - assert s.startswith('\\begin{alltt}\nThis is'), \ - "Unexpected start of environment" - assert s.endswith('two lines\n\\end{alltt}'), \ - "Unexpected end of environment" + assert s.startswith("\\begin{alltt}\nThis is"), "Unexpected start of environment" + assert s.endswith("two lines\n\\end{alltt}"), "Unexpected end of environment" diff --git a/tests/test_forced_dumps_implementation.py b/tests/test_forced_dumps_implementation.py index c65d3032..cebaab9a 100644 --- a/tests/test_forced_dumps_implementation.py +++ b/tests/test_forced_dumps_implementation.py @@ -1,6 +1,7 @@ -from pylatex.base_classes import LatexObject from pytest import raises +from pylatex.base_classes import LatexObject + class BadObject(LatexObject): pass diff --git a/tests/test_inheritance.py b/tests/test_inheritance.py index ebb84688..a708f261 100644 --- a/tests/test_inheritance.py +++ b/tests/test_inheritance.py @@ -4,7 +4,6 @@ class TestInheritance(unittest.TestCase): - def test_latex_name(self): class MyDoc(Document): def __init__(self): diff --git a/tests/test_jobname.py b/tests/test_jobname.py index 83ae89f7..90af0c8c 100755 --- a/tests/test_jobname.py +++ b/tests/test_jobname.py @@ -7,30 +7,30 @@ def test(): - doc = Document('jobname_test', data=['Jobname test']) + doc = Document("jobname_test", data=["Jobname test"]) doc.generate_pdf() - assert os.path.isfile('jobname_test.pdf') + assert os.path.isfile("jobname_test.pdf") - os.remove('jobname_test.pdf') + os.remove("jobname_test.pdf") - folder = 'tmp_jobname' + folder = "tmp_jobname" os.makedirs(folder) - path = os.path.join(folder, 'jobname_test_dir') + path = os.path.join(folder, "jobname_test_dir") - doc = Document(path, data=['Jobname test dir']) + doc = Document(path, data=["Jobname test dir"]) doc.generate_pdf() - assert os.path.isfile(path + '.pdf') + assert os.path.isfile(path + ".pdf") shutil.rmtree(folder) - folder = 'tmp_jobname2' + folder = "tmp_jobname2" os.makedirs(folder) - path = os.path.join(folder, 'jobname_test_dir2') + path = os.path.join(folder, "jobname_test_dir2") - doc = Document(path, data=['Jobname test dir']) - doc.generate_pdf(os.path.join(folder, '')) + doc = Document(path, data=["Jobname test dir"]) + doc.generate_pdf(os.path.join(folder, "")) - assert os.path.isfile(path + '.pdf') + assert os.path.isfile(path + ".pdf") shutil.rmtree(folder) diff --git a/tests/test_no_fontenc.py b/tests/test_no_fontenc.py index c6d898d6..8f1eca04 100644 --- a/tests/test_no_fontenc.py +++ b/tests/test_no_fontenc.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- r"""A test to make sure the document compiles with fontenc set to `None`.""" -from pylatex.base_classes import Arguments from pylatex import Document +from pylatex.base_classes import Arguments -doc = Document('no_fontenc', fontenc=None) -doc.append('test text') +doc = Document("no_fontenc", fontenc=None) +doc.append("test text") # Make sure fontenc isn't used -assert not any([p.arguments == Arguments('fontenc') for p in doc.packages]) +assert not any([p.arguments == Arguments("fontenc") for p in doc.packages]) doc.generate_pdf(clean=True, clean_tex=False, silent=False) diff --git a/tests/test_no_inputenc.py b/tests/test_no_inputenc.py index 304d3c9d..47e37b92 100644 --- a/tests/test_no_inputenc.py +++ b/tests/test_no_inputenc.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- r"""A test to make sure the document compiles with inputenc set to `None`.""" -from pylatex.base_classes import Arguments from pylatex import Document +from pylatex.base_classes import Arguments -doc = Document('no_inputenc', inputenc=None) -doc.append('test text') +doc = Document("no_inputenc", inputenc=None) +doc.append("test text") # Make sure inputenc isn't used -assert not any([p.arguments == Arguments('inputenc') for p in doc.packages]) +assert not any([p.arguments == Arguments("inputenc") for p in doc.packages]) doc.generate_pdf(clean=True, clean_tex=False, silent=False) diff --git a/tests/test_no_list_as_data.py b/tests/test_no_list_as_data.py index afc2228f..7f44ddd5 100644 --- a/tests/test_no_list_as_data.py +++ b/tests/test_no_list_as_data.py @@ -1,15 +1,14 @@ -from pylatex import Document, Section, Subsection, Command +from pylatex import Command, Document, Section, Subsection def test(): doc = Document() - Subsection('Only a single string', data='Some words') + Subsection("Only a single string", data="Some words") - sec1 = Section('Only contains one subsection', data='Subsection') + sec1 = Section("Only contains one subsection", data="Subsection") - sec2 = Section('Only a single italic command', data=Command('textit', - 'Hey')) - sec2.append('something else that is not italic') + sec2 = Section("Only a single italic command", data=Command("textit", "Hey")) + sec2.append("something else that is not italic") doc.append(sec1) doc.append(sec2) diff --git a/tests/test_no_lmodern.py b/tests/test_no_lmodern.py index a0b23a0b..66bf4795 100644 --- a/tests/test_no_lmodern.py +++ b/tests/test_no_lmodern.py @@ -3,7 +3,7 @@ from pylatex import Document -doc = Document('no_lmodern', lmodern=False) -doc.append('test text') +doc = Document("no_lmodern", lmodern=False) +doc.append("test text") doc.generate_pdf(clean=True, clean_tex=False, silent=False) diff --git a/tests/test_pictures.py b/tests/test_pictures.py index e287624f..1b222e35 100644 --- a/tests/test_pictures.py +++ b/tests/test_pictures.py @@ -1,18 +1,18 @@ #!/usr/bin/env python +import os + from pylatex import Document, Section from pylatex.figure import Figure -import os def test(): doc = Document() - section = Section('Multirow Test') + section = Section("Multirow Test") figure = Figure() - image_filename = os.path.join(os.path.dirname(__file__), - '../examples/kitten.jpg') + image_filename = os.path.join(os.path.dirname(__file__), "../examples/kitten.jpg") figure.add_image(image_filename) - figure.add_caption('Whoooo an imagage of a pdf') + figure.add_caption("Whoooo an imagage of a pdf") section.append(figure) doc.append(section) diff --git a/tests/test_quantities.py b/tests/test_quantities.py index 221da89e..0780df2b 100644 --- a/tests/test_quantities.py +++ b/tests/test_quantities.py @@ -1,39 +1,42 @@ # -*- coding: utf-8 -*- import quantities as pq -from pylatex.quantities import _dimensionality_to_siunitx, Quantity +from pylatex.quantities import Quantity, _dimensionality_to_siunitx def test_quantity(): - v = 1 * pq.m/pq.s + v = 1 * pq.m / pq.s q1 = Quantity(v) - assert q1.dumps() == r'\SI{1.0}{\meter\per\second}' + assert q1.dumps() == r"\SI{1.0}{\meter\per\second}" q2 = Quantity(v, format_cb=lambda x: str(int(x))) - assert q2.dumps() == r'\SI{1}{\meter\per\second}' + assert q2.dumps() == r"\SI{1}{\meter\per\second}" - q3 = Quantity(v, options={'zero-decimal-to-integer': 'true'}) - ref = r'\SI[zero-decimal-to-integer=true]{1.0}{\meter\per\second}' + q3 = Quantity(v, options={"zero-decimal-to-integer": "true"}) + ref = r"\SI[zero-decimal-to-integer=true]{1.0}{\meter\per\second}" assert q3.dumps() == ref def test_quantity_float(): q1 = Quantity(42.0) - assert q1.dumps() == r'\num{42.0}' + assert q1.dumps() == r"\num{42.0}" def test_quantity_uncertain(): - t = pq.UncertainQuantity(7., pq.second, 1.) + t = pq.UncertainQuantity(7.0, pq.second, 1.0) q1 = Quantity(t) - assert q1.dumps() == r'\SI{7.0 +- 1.0}{\second}' + assert q1.dumps() == r"\SI{7.0 +- 1.0}{\second}" def test_dimensionality_to_siunitx(): - assert _dimensionality_to_siunitx((pq.volt/pq.kelvin).dimensionality) == \ - r'\volt\per\Kelvin' + assert ( + _dimensionality_to_siunitx((pq.volt / pq.kelvin).dimensionality) + == r"\volt\per\Kelvin" + ) -if __name__ == '__main__': + +if __name__ == "__main__": test_quantity() test_quantity_uncertain() test_dimensionality_to_siunitx() diff --git a/tests/test_utils_dumps_list.py b/tests/test_utils_dumps_list.py index 9e1bcf48..02b86e20 100644 --- a/tests/test_utils_dumps_list.py +++ b/tests/test_utils_dumps_list.py @@ -1,18 +1,20 @@ #!/usr/bin/env python -from pylatex.utils import dumps_list from pylatex.basic import MediumText +from pylatex.utils import dumps_list def test_mapper(): - assert dumps_list(['Test', 'text'], mapper=MediumText) == \ - '''\\begin{large}% + assert ( + dumps_list(["Test", "text"], mapper=MediumText) + == """\\begin{large}% Test% \\end{large}% \\begin{large}% text% -\\end{large}''' +\\end{large}""" + ) -if __name__ == '__main__': +if __name__ == "__main__": test_mapper() diff --git a/tests/test_utils_escape_latex.py b/tests/test_utils_escape_latex.py index 147e0fb8..c9d4344a 100644 --- a/tests/test_utils_escape_latex.py +++ b/tests/test_utils_escape_latex.py @@ -6,9 +6,10 @@ def test(): doc = Document("utils_escape_latex") - section = Section('Escape LaTeX characters test') + section = Section("Escape LaTeX characters test") - text = escape_latex('''\ + text = escape_latex( + """\ & (ampersand) % (percent) $ (dollar) @@ -23,12 +24,14 @@ def test(): a\xA0a (non breaking space) [ (left bracket) ] (right bracket) - ''') + """ + ) section.append(text) doc.append(section) doc.generate_pdf() -if __name__ == '__main__': + +if __name__ == "__main__": test() diff --git a/tests/test_utils_fix_filename.py b/tests/test_utils_fix_filename.py index 6a994d93..dfc44fcf 100644 --- a/tests/test_utils_fix_filename.py +++ b/tests/test_utils_fix_filename.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import os + from pylatex.utils import fix_filename @@ -18,9 +19,9 @@ def test_two_dots(): fname = "aa.a.a" original_os_name = os.name try: - os.name = 'posix' + os.name = "posix" assert fix_filename(fname) == "{aa.a}.a" - os.name = 'nt' + os.name = "nt" assert fix_filename(fname) == "aa.a.a" finally: os.name = original_os_name @@ -53,5 +54,6 @@ def test_dots_in_path_and_multiple_in_filename(): def test_tilde_in_filename(): fname = "/etc/local/foo.bar.baz/foo~1/document.pdf" - assert (fix_filename(fname) == - '\detokenize{/etc/local/foo.bar.baz/foo~1/document.pdf}') + assert ( + fix_filename(fname) == "\detokenize{/etc/local/foo.bar.baz/foo~1/document.pdf}" + ) diff --git a/tests/test_utils_latex_item_to_string.py b/tests/test_utils_latex_item_to_string.py index 68f93e80..730bd4df 100644 --- a/tests/test_utils_latex_item_to_string.py +++ b/tests/test_utils_latex_item_to_string.py @@ -1,13 +1,13 @@ #!/usr/bin/env python -from pylatex.utils import _latex_item_to_string from pylatex.base_classes import LatexObject +from pylatex.utils import _latex_item_to_string -TEST_STR = 'hello' +TEST_STR = "hello" def test_string(): - name = 'abc' + name = "abc" assert _latex_item_to_string(name) == name