Skip to content

Commit ec629f8

Browse files
committed
Add examples infrastructure
* Uses the `minted` package to include formatted source code segments * Uses a preprocessor script to extract snippets from code examples Signed-off-by: Joshua Hursey <[email protected]>
1 parent ae76313 commit ec629f8

File tree

7 files changed

+566
-15
lines changed

7 files changed

+566
-15
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
*.synctex.gz
1212
*.DS_Store
1313
*.xwm
14+
*.mw
1415
datetime-defaults.sty
1516
datetime.sty
1617
check-openpmix
1718
figs/~$drawings.pptx
1819
*.loc
1920
*.soc
21+
_minted*
22+
sources/_autogen_/

Makefile

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Makefile for the PMIx Standard document in LaTex format.
22
# For more information, see the master document, pmix-standard.tex.
33

4+
LATEX_C=pdflatex -shell-escape -file-line-error
5+
46
version=4.0
57
default: pmix-standard.pdf
68

@@ -27,11 +29,8 @@ CHAPTERS= \
2729
App_Python.tex \
2830
Acknowledgements.tex
2931

30-
SOURCES=
31-
# SOURCES=sources/*.c \
32-
# sources/*.cpp \
33-
# sources/*.f90 \
34-
# sources/*.f
32+
SOURCES=sources/*.c \
33+
sources/*.py \
3534

3635
INTERMEDIATE_FILES=pmix-standard.pdf \
3736
pmix-standard.toc \
@@ -45,27 +44,34 @@ INTERMEDIATE_FILES=pmix-standard.pdf \
4544
pmix-standard.blg \
4645
pmix-standard.synctex.gz \
4746
pmix-standard.xwm \
48-
*.idx *.ilg *.ind
47+
pmix-standard.mw \
48+
pmix-standard.loc \
49+
pmix-standard.soc \
50+
*.idx *.ilg *.ind \
51+
_minted-* \
52+
sources/_autogen_
4953

5054
all: pmix-standard.pdf
5155

5256
pmix-standard.pdf: $(CHAPTERS) $(SOURCES) pmix.sty pmix-standard.tex figs/pmix-logo.png
53-
rm -f $(INTERMEDIATE_FILES)
57+
rm -rf $(INTERMEDIATE_FILES)
5458
@echo "-------------------------------------------------------------"
5559
@echo "If error occurs check pmix-standard.log and pmix-standard.ind"
5660
@echo "-------------------------------------------------------------"
61+
@echo "====> Preprocess Examples"
62+
@./bin/process-example.py $(SOURCES)
5763
@echo "====> Building 1/4"
58-
pdflatex -interaction=batchmode -file-line-error pmix-standard.tex || \
59-
pdflatex -interaction=errorstopmode -file-line-error pmix-standard.tex < /dev/null
64+
$(LATEX_C) -interaction=batchmode pmix-standard.tex || \
65+
$(LATEX_C) -interaction=errorstopmode pmix-standard.tex < /dev/null
6066
@echo "====> Building 2/4 (bibtex)"
6167
bibtex pmix-standard < /dev/null
6268
@echo "====> Building 3/4"
63-
pdflatex -interaction=batchmode -file-line-error pmix-standard.tex || \
64-
pdflatex -interaction=errorstopmode -file-line-error pmix-standard.tex < /dev/null
69+
$(LATEX_C) -interaction=batchmode pmix-standard.tex || \
70+
$(LATEX_C) -interaction=errorstopmode pmix-standard.tex < /dev/null
6571
@echo "====> Building 4/4"
66-
pdflatex -interaction=batchmode -file-line-error pmix-standard.tex || \
67-
pdflatex -interaction=errorstopmode -file-line-error pmix-standard.tex < /dev/null
68-
pdflatex -interaction=batchmode -file-line-error pmix-standard.tex
72+
$(LATEX_C) -interaction=batchmode pmix-standard.tex || \
73+
$(LATEX_C) -interaction=errorstopmode pmix-standard.tex < /dev/null
74+
$(LATEX_C) -interaction=batchmode pmix-standard.tex
6975
@./bin/check-doc.sh
7076
@echo "====> Success"
7177
@cp pmix-standard.pdf pmix-standard-${version}.pdf
@@ -93,4 +99,4 @@ check-openpmix: pmix-standard.pdf FORCECHECK
9399
@./bin/check-openpmix.py
94100

95101
clean:
96-
rm -f $(INTERMEDIATE_FILES) pmix-standard-*.pdf
102+
rm -rf $(INTERMEDIATE_FILES) pmix-standard-*.pdf

bin/process-example.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#!/usr/bin/python -u
2+
3+
#
4+
# Recognized tags
5+
# <EG BEGIN ID="string"> : Start a block
6+
# <EG END ID="string"> : End a block
7+
# Rules:
8+
# - The tag strings, regardless of ID, are removed from the final output
9+
# - If multiple blocks with the same ID exist then they are concatinated together
10+
# - ID can contain alphabetic and numberic characters and the following symbols: . _
11+
# - Quote marks are stripped out
12+
#
13+
import sys
14+
import os
15+
import re
16+
import argparse
17+
import subprocess
18+
import shutil
19+
20+
EG_BEGIN_STR="EG BEGIN ID="
21+
EG_END_STR="EG END ID="
22+
EG_ID_PATTERN="[A-Za-z0-9_\.\"]"
23+
24+
25+
class Example:
26+
"""An example from the source code"""
27+
eid = None
28+
active = False
29+
filename = None
30+
code_block = ""
31+
32+
def __init__(self):
33+
self.eid = ""
34+
self.active = False
35+
self.filename = None
36+
self.code_block = ""
37+
38+
def __str__(self):
39+
return "in_fname=[%s] id=[%s]" % (self.filename, self.eid)
40+
41+
def get_out_fname(self):
42+
f_id = self.eid.replace('"', '')
43+
return os.path.basename(self.filename) + "_" + f_id
44+
45+
def append_line(self, line):
46+
self.code_block = self.code_block + line
47+
if line.endswith("\n") is False:
48+
self.code_block = self.code_block + "\n"
49+
50+
def get_code_block(self):
51+
final_block = ""
52+
min_lead_spaces = 10000
53+
lines = 0
54+
total_lines = 0
55+
skip_last_lines = 0
56+
57+
# First pass to find min spacing
58+
for line in self.code_block.splitlines(True):
59+
if re.search(r"\s*"+EG_BEGIN_STR, line) is not None:
60+
continue
61+
if re.search(r"\s*"+EG_END_STR, line) is not None:
62+
continue
63+
64+
total_lines = total_lines + 1
65+
# Skip empty lines
66+
if len(line) <= 1:
67+
skip_last_lines = skip_last_lines + 1
68+
continue
69+
else:
70+
skip_last_lines = 0
71+
72+
m = re.match(r"^([ \t]+)", line)
73+
if m is not None:
74+
c_len = len(m.group(1))
75+
if c_len > 1:
76+
min_lead_spaces = min(c_len, min_lead_spaces)
77+
else:
78+
# Indicates that there is a line that does not have leading spaces, but is not empty
79+
min_lead_spaces = 0
80+
81+
# Next pass to build the string
82+
lines = 0
83+
for line in self.code_block.splitlines(True):
84+
if re.search(r"\s*"+EG_BEGIN_STR, line) is not None:
85+
continue
86+
if re.search(r"\s*"+EG_END_STR, line) is not None:
87+
continue
88+
89+
# Clear off trailing empty lines
90+
if total_lines - skip_last_lines == lines:
91+
break
92+
lines = lines + 1
93+
94+
m = re.match(r"^([ \t]+)", line)
95+
if m is not None:
96+
line = line[min_lead_spaces:]
97+
final_block = final_block + line
98+
99+
return final_block
100+
101+
102+
def process_file(filename):
103+
"""Process all of the key/value splitting in the file"""
104+
all_examples = {}
105+
eg_id = None
106+
107+
with open(filename, 'r') as fd:
108+
for line in fd:
109+
line = line.rstrip()
110+
m = re.search(r"\s*"+EG_BEGIN_STR+"("+EG_ID_PATTERN+"*)", line)
111+
if m is not None:
112+
eg_id = m.group(1)
113+
# Find this object and update it
114+
found = False
115+
for example_id in all_examples:
116+
if example_id == eg_id:
117+
example = all_examples[example_id]
118+
example.active = True
119+
example.append_line(line)
120+
found = True
121+
# Insert it if not found
122+
if found is False:
123+
x = Example()
124+
x.eid = eg_id
125+
x.active = True
126+
x.append_line(line)
127+
x.filename = filename
128+
all_examples[eg_id] = x
129+
continue
130+
131+
m = re.search(r"\s*"+EG_END_STR+"("+EG_ID_PATTERN+"*)", line)
132+
if m is not None:
133+
eg_id = m.group(1)
134+
# Find this object and update it
135+
for example_id in all_examples:
136+
if example_id == eg_id:
137+
example = all_examples[example_id]
138+
example.active = False
139+
example.append_line("") # Add an empty line
140+
continue
141+
142+
for example_id in all_examples:
143+
example = all_examples[example_id]
144+
if example.active is True:
145+
example.append_line(line)
146+
147+
return all_examples
148+
149+
150+
if __name__ == "__main__":
151+
#
152+
# Command line parsing
153+
#
154+
parser = argparse.ArgumentParser(description="PMIx Standard Example Preprocessor")
155+
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
156+
parser.add_argument("files", nargs='+', help="List of files to process")
157+
parser.parse_args()
158+
args = parser.parse_args()
159+
160+
#
161+
# Create a temporary directory to store the snippets
162+
#
163+
gen_dir = "sources/_autogen_"
164+
if os.access(gen_dir, os.W_OK) is False:
165+
os.makedirs(gen_dir)
166+
167+
#
168+
# Iterate through all examples and split out the snippets
169+
#
170+
for f in args.files:
171+
if os.path.exists(f) is False:
172+
print("ERROR: File does not exist: %s" % (f))
173+
sys.exit(1)
174+
175+
print("Processing File: %s" % (f))
176+
example_blocks = process_file(f)
177+
for k, example in example_blocks.items():
178+
out_fname = gen_dir + "/" + example.get_out_fname()
179+
print("\tExample: %s -- Stored in %s" % (example, out_fname))
180+
if args.verbose:
181+
print("CODE BLOCK")
182+
print("-" * 50)
183+
print(example.get_code_block()),
184+
print("-" * 50)
185+
with open(out_fname, 'w') as fd:
186+
fd.write("%s" % (example.get_code_block()))
187+
188+
sys.exit(0)

pmix.sty

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,3 +760,109 @@
760760
\newcounter{pycounter}
761761
\newcommand{\pylabel}[1]{\refstepcounter{pycounter} \label{appB:#1}}
762762
\newcommand{\refpy}[1]{\hyperref[appB:#1]{\code{#1}}}
763+
764+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765+
% Code examples
766+
% Install: (if using Python 3 then use pip3)
767+
% sudo pip2 install --upgrade pip setuptools
768+
% sudo pip2 install Pygments
769+
% Ubuntu:
770+
% sudo apt update
771+
% sudo apt install python-pip
772+
% sudo pip3 install Pygments
773+
%
774+
% package to work around "No room for a new \write" message
775+
\usepackage{morewrites}
776+
\morewritessetup{allocate=10}
777+
% package for code highlighting
778+
\usepackage[chapter]{minted}
779+
\usemintedstyle{default}
780+
\usepackage{xcolor}
781+
782+
% Background: Light Gray
783+
\definecolor{pmixbg}{rgb}{0.95,0.95,0.95}
784+
% Highlight: Yellow
785+
\definecolor{pmixhl}{rgb}{0.95,0.95,0}
786+
787+
%-----------------------
788+
% C Code
789+
%
790+
% block of code
791+
% \begin{pmixCodeC}
792+
% return 0;
793+
% \end{pmixCodeC}
794+
%
795+
% Inline C code
796+
% The code \pmixCodeInlineC{printf("Hello World");} prints ``Hello World''.
797+
%
798+
% Import C code from a file:
799+
% - Whole file: \pmixCodeImportC{sources/hello.c}
800+
% - Selection of a file: \pmixCodeImportC[firstline=73, lastline=84]{sources/hello.c}
801+
% - Highlight selection of a file: \pmixCodeImportC[firstline=73, lastline=84, highlightlines={2-4,6}]{sources/hello.c}
802+
%
803+
\newminted[pmixCodeC]{c}{fontsize=\small,
804+
bgcolor=pmixbg,
805+
highlightcolor=pmixhl,
806+
breaklines,
807+
autogobble,
808+
linenos,
809+
numbersep=3pt,
810+
firstnumber=1}
811+
\newmintinline[pmixCodeInlineC]{c}{bgcolor=pmixbg,
812+
highlightcolor=pmixhl,
813+
breaklines,
814+
autogobble,
815+
linenos,
816+
firstnumber=1}
817+
\newmintedfile[pmixCodeImportC]{c}{fontsize=\small,
818+
bgcolor=pmixbg,
819+
highlightcolor=pmixhl,
820+
breaklines,
821+
autogobble,
822+
linenos,
823+
numbersep=3pt,
824+
firstnumber=1}
825+
826+
%-----------------------
827+
% Python Code
828+
%
829+
% block of code
830+
% \begin{pmixCodePy}
831+
% print("Hello")
832+
% \end{pmixCodePy}
833+
%
834+
% Inline C code
835+
% The code \pmixCodeInlinePy{print("Hello World")} prints ``Hello World''.
836+
%
837+
% Import C code from a file:
838+
% - Whole file: \pmixCodeImportPy{sources/hello.py}
839+
% - Selection of a file: \pmixCodeImportPy[firstline=73, lastline=84]{sources/hello.py}
840+
% - Highlight selection of a file: \pmixCodeImportPy[firstline=73, lastline=84, highlightlines={2-4,6}]{sources/hello.py}
841+
%
842+
\newminted[pmixCodepPy]{python}{fontsize=\small,
843+
bgcolor=pmixbg,
844+
highlightcolor=pmixhl,
845+
breaklines,
846+
autogobble,
847+
linenos,
848+
numbersep=3pt,
849+
firstnumber=1}
850+
\newmintinline[pmixCodeInlinePy]{python}{bgcolor=pmixbg,
851+
highlightcolor=pmixhl,
852+
breaklines,
853+
autogobble,
854+
linenos,
855+
firstnumber=1}
856+
\newmintedfile[pmixCodeImportPy]{python}{fontsize=\small,
857+
bgcolor=pmixbg,
858+
highlightcolor=pmixhl,
859+
breaklines,
860+
autogobble,
861+
linenos,
862+
numbersep=3pt,
863+
firstnumber=1}
864+
865+
866+
867+
868+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

0 commit comments

Comments
 (0)