Browse Source

documentation

adam j hartz 2 weeks ago
parent
commit
a98bde271b
3 changed files with 78 additions and 241 deletions
  1. 76
    239
      catsoop/language.py
  2. 1
    1
      website/docs/api/content.py
  3. 1
    1
      website/docs/preload.py

+ 76
- 239
catsoop/language.py View File

@@ -14,136 +14,9 @@
14 14
 # You should have received a copy of the GNU Affero General Public License
15 15
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16
 """
17
-SUMMARY OF THIS FILE
18
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19
-
20
-   The overall flow when parsing a page is:
21
-
22
-0. Label all the <python> tags with their line number and the current file; e.g.
23
-
24
-      <python>
25
-
26
-   on line 67 in file "COURSE/information/content.md" becomes
27
-
28
-      <python cs_internal_sourcefile="COURSE/information/content.md" cs_internal_linenumber=67>
29
-
30
-½. <comment> elements are removed.
31
-
32
-1. <include> tags are replaced by the contents of the files they reference.
33
-   Before substitution into the current document, step 0 is run analagously on
34
-   the contents of each file.
35
-
36
-1½. <comment> elements are removed again.
37
-
38
-2. <python> tags are replaced by the output of the code inside them.
39
-
40
-   [At this point, any `post_load` events occur.]
41
-
42
-3. `cs_transform_source : string -> string` is run on the entire page.
43
-
44
-4a. Custom tags are handled. Each tag name in `cs_custom_tags`
45
-
46
-      cs_custom_tags : {string => {'open' => tag_replmt
47
-                                   'body' => body_replmt
48
-                                   'close' => tag_replmt
49
-                                   'markdown' => bool
50
-                                   'verbatim' => bool}}
51
-
52
-      tag_replmt = string | params * context -> string
53
-
54
-      body_replmt = string | string * params * context -> string
55
-
56
-      params = {string => string}
57
-
58
-   Here's an example:
59
-
60
-      # <note>hello!</note>
61
-      #   will become
62
-      # <b>Note:</b> <span style="color: #808080;">hello!</span>
63
-      #
64
-      # <note color="#333366">sample text</note>
65
-      #   will become
66
-      # <b>Note:</b> <span style="color: #333366;">sample text</span>
67
-
68
-      def note_open(params, context):
69
-        color = params["color"] if "color" in params else "#808080"
70
-        return '<b>Note:</b> <span style="color:%s">' % color
71
-
72
-      cs_custom_tags = {"note": ('open': note_open, 'close': "</span>")}
73
-
74
-      # Instead of using None, we could have written
75
-      #
76
-      # def note_body(text, params, context):
77
-      #     return text
78
-      # cs_custom_tags = {"note": {'open': note_open, 'body': note_body, 'close': "</span>")}
79
-      #
80
-      # and achieved the same effect.
81
-
82
-   For each top-level custom element in the content file,
83
-
84
-    - everything between the opening and closing tags (including the tags them-
85
-      selves) is removed and replaced with a unique, random string ("uid").
86
-
87
-    - The replacement opening tag and body are generated using the corresponding
88
-      functions in `cs_custom_tags`.
89
-
90
-    - Step 4 is recursively run on the replacement body.
91
-
92
-    - The results are saved in the `custom_elements` dict, keyed on the uid.
93
-
94
-4b. If the content file is in markdown, modify the source up to this point by
95
-   calling the `md` function.
96
-
97
-4c. Each uid in `custom_elements` is replaced by its corresponding text.
98
-
99
-5. `cs_course_handle_custom_tags` is deprecated, but for legacy support its
100
-   behavior is replicated here.
101
-
102
-6. Other CAT-SOOP-specific tags are implemented using BeautifulSoup.
103
-   If the function `cs_transform_tree : tree -> tree` is defined, it is
104
-   called before the built-ins are applied.
105
-
106
-7. the page source is split into the format required by __HANDLERS__ and placed
107
-   in `cs_problem_spec`.
108
-
109
-
110
-LIST OF BUILT-IN CUSTOM TAGS IN `cs_custom_tags`
111
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112
-
113
-   Name                Attributes          Markdown    Verbatim    Priority
114
-   ----------------    ----------------    --------    --------    --------
115
-   comment                                    N           Y           20
116
-   question            qtype                  N           Y           15
117
-   script                                     N           Y           14
118
-   pre                                        N           Y           13
119
-   displaymath                                N           Y           12
120
-   math                                       N           Y           11
121
-   chapter*                                   Y           N            9
122
-   section*                                   Y           N            8
123
-   subsection*                                Y           N            7
124
-   subsubsection*                             Y           N            6
125
-
126
-
127
-
128
-LIST OF BUILT-IN CUSTOM TAGS IMPLEMENTED WITH BEAUTIFULSOUP
129
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130
-
131
-   Name                Attributes
132
-   ----------------    ------------------------
133
-   showhide            label=...
134
-   displaymath
135
-   math
136
-   tableofcontents
137
-   chapter
138
-   section
139
-   subsection
140
-   subsubsection
141
-   ref                 label=...
142
-   footnote
143
-
17
+Handling of the CAT-SOOP specification language(s): Markdown, XML, and Python
144 18
 """
145 19
 
146
-
147 20
 import re
148 21
 import os
149 22
 import sys
@@ -172,34 +45,49 @@ LOGGER = debug_log.LOGGER
172 45
 
173 46
 
174 47
 source_formats = ("md", "xml", "py")
48
+"""
49
+Tuple of the CAT-SOOP source formats, in the order they should be considered.
50
+"""
175 51
 
176 52
 
177 53
 class CatsoopSyntaxError(Exception):
54
+    """
55
+    Exception raised due to invalid CAT-SOOP syntax
56
+    """
57
+
178 58
     pass
179 59
 
180 60
 
181 61
 class CatsoopInternalError(Exception):
62
+    """
63
+    Exception raised when input cannot be properly handled (but is
64
+    syntactically valid)
65
+    """
66
+
182 67
     pass
183 68
 
184 69
 
185 70
 _nodoc = {
186
-    "source_formats",
187
-    "html_from_source",
71
+    "annotate_python",
72
+    "remove_comments",
73
+    "replace_include_tags",
74
+    "replace_python_tags",
75
+    "replace_custom_tags",
188 76
     "replace_toplevel_element",
77
+    "execute_python",
189 78
     "parse_tag",
190
-    "___valid_qname",
191
-    "___reformat_tag",
79
+    "build_tree",
192 80
     "build_question",
193 81
     "cs_custom_tags_builtins",
194
-    "___indent_regex",
195
-    "___string_regex",
196
-    "___ascii_letters",
197
-    "___tab_replacer",
198
-    "___replace_indentation_tabs",
199 82
     "remove_common_leading_whitespace",
200 83
     "indent_python",
201 84
     "legacy_tree",
202 85
     "handle_math_tags",
86
+    "BeautifulSoup",
87
+    "LOGGER",
88
+    "StringIO",
89
+    "html_format",
90
+    "clear_info",
203 91
 }
204 92
 
205 93
 # Interface Functions ----------------------------------------------------------
@@ -209,6 +97,19 @@ _nodoc = {
209 97
 
210 98
 
211 99
 def html_from_source(context, source, override_format=None):
100
+    """
101
+    Given a piece of text representing source code, return the associated HTML
102
+    (determined by calling `gather_page` and `assemble_page`).
103
+
104
+    * `context`: the context of this request
105
+    * `source`:  the input to be formatted (generally a page or a small snippet
106
+        of source)
107
+
108
+    **Optional Paramters:**
109
+
110
+    * `override_format`: The format that `source` should be considered to have.
111
+        Defaults to `context["cs_source_format"]
112
+    """
212 113
     return assemble_page(
213 114
         context, gather_page(context, source, override_format), override_format, False
214 115
     )
@@ -216,21 +117,20 @@ def html_from_source(context, source, override_format=None):
216 117
 
217 118
 def gather_page(context, source, override_format=None):
218 119
     """
219
-    Gathers all the sources for a page.
120
+    In a given piece of source text, replaces includes and annotates python
121
+    tags.
220 122
 
221 123
     **Parameters:**
222
-        `context`           the context of this request (should be `into` from loader.py)
223
-        `source`            the text of this request
124
+
125
+    * `context`: the context of this request
126
+    * `source`:  the input to be formatted
127
+
224 128
     **Optional Paramters:**
225
-        `override_format`   the format that `source` should be considered to have, regardless of `cs_source_format`
226
-    **Returns:**
227
-        `source`    the modified text
228
-    **Calls:**
229
-        (0) `annotate_python`
230
-            `remove_comments`
231
-        (1) `replace_include_tags`
232
-            `remove_comments`
233
-        (2) `replace_python_tags`
129
+
130
+    * `override_format`: The format that `source` should be considered to have.
131
+        Defaults to `context["cs_source_format"]
132
+
133
+    **Returns:** the modified text
234 134
     """
235 135
     source_format = override_format or context["cs_source_format"]
236 136
 
@@ -255,21 +155,24 @@ def gather_page(context, source, override_format=None):
255 155
 
256 156
 def assemble_page(context, source, override_format=None, set_problem_spec=True):
257 157
     """
258
-    Assembles the final HTML of a page.
158
+    Assembles the final HTML of a page by running python code, handling custom
159
+    tags, and handling built-in tags.
259 160
 
260 161
     **Parameters:**
261
-        `context`           the context of this request (should be `into` from loader.py)
262
-        `source`            the text of this request
162
+
163
+    * `context`:  The context of this request
164
+    * `source`: The source of a page, which should now have no includes
165
+
263 166
     **Optional Paramters:**
264
-        `override_format`   the format that `source` should be considered to have, regardless of `cs_source_format`
265
-        `set_problem_spec`  a boolean that controls whether `cs_problem_spec` is set or not
266
-    **Returns:**
267
-        `source`            the modified text
268
-    **Calls:**
269
-        (3) `context["cs_transform_source"]`
270
-        (4) `replace_custom_tags`
271
-        (5) `context["cs_course_handle_custom_tags"]`
272
-        (6) `build_tree`
167
+
168
+    * `override_format`: the format that `source` should be considered to have.
169
+        Defaults to `context[cs_source_format"]
170
+
171
+    * `set_problem_spec`: a boolean that controls whether `cs_problem_spec` is
172
+        set or not.  Defaults to `True`, but should be set to `False` in the
173
+        context of evaluating CAT-SOOP source outside of a normal page load.
174
+
175
+    **Returns:** a string containing the final HTML generated from the input.
273 176
     """
274 177
     source_format = override_format or context["cs_source_format"]
275 178
 
@@ -335,26 +238,11 @@ def assemble_page(context, source, override_format=None, set_problem_spec=True):
335 238
     return source
336 239
 
337 240
 
338
-# Top Level Functions ----------------------------------------------------------
339
-#   annotate_python
340
-#   remove_comments
341
-#   replace_include_tags
342
-#   replace_python_tags
343
-#   replace_custom_tags
344
-#   build_tree
345
-
346
-
347 241
 def annotate_python(fn, source):
348 242
     """
349
-    Modifies `<python>` tags to include two parameters:
243
+    Helper, modifies `<python>` tags to include two parameters:
350 244
         cs_internal_sourcefile  the name of source file that this tag is from
351 245
         cs_internal_linenumber  the line number of this tag in its source file
352
-
353
-    **Parameters:**
354
-        `fn`        the name of the original file containing the tag
355
-        `source`    the text of this request
356
-    **Returns:**
357
-        `source`    the modified text
358 246
     """
359 247
     expr = re.compile(r"< *(python|printf)( *| +[^>]+)>")
360 248
 
@@ -375,12 +263,7 @@ def annotate_python(fn, source):
375 263
 
376 264
 def remove_comments(source):
377 265
     """
378
-    Removes `<comment>` elements.
379
-
380
-    **Parameters:**
381
-        `source`    the text of this request
382
-    **Returns:**
383
-        `source`    the modified text
266
+    Helper, removes `<comment>` elements.
384 267
     """
385 268
     subs_func = lambda opening, body, closing: ""
386 269
     source = replace_toplevel_element(source, "comment", subs_func)
@@ -389,13 +272,8 @@ def remove_comments(source):
389 272
 
390 273
 def replace_include_tags(context, source):
391 274
     """
392
-    Replaces `<include>` tags with the contents of the files they reference.
393
-
394
-    **Parameters:**
395
-        `context`   the context of this request
396
-        `source`    the text of this request
397
-    **Returns:**
398
-        `source`    the modified text
275
+    Helper, replaces `<include>` tags with the contents of the files they
276
+    reference.
399 277
     """
400 278
     # handle paths relative to here unless given an absolute path
401 279
     def subs_func(opening, body, closing):
@@ -433,14 +311,9 @@ def replace_include_tags(context, source):
433 311
 
434 312
 def replace_python_tags(context, source):
435 313
     """
436
-    Replaces `<python>` elements with the output of the code within, and `@{}`
437
-    and `<printf>` with the representation of the variable they reference.
438
-
439
-    **Parameters:**
440
-        `context`   the context of this request
441
-        `source`    the text of this request
442
-    **Returns:**
443
-        `source`    the modified text
314
+    Helper, replaces `<python>` elements with the output of the code within,
315
+    and `@{}` and `<printf>` with the representation of the variable they
316
+    reference.
444 317
     """
445 318
     # The next 11 lines are legacy code, and
446 319
     # should probably be rewritten eventually.
@@ -554,20 +427,9 @@ def replace_python_tags(context, source):
554 427
 
555 428
 def replace_custom_tags(context, source, source_format, disable_markdown=False):
556 429
     """
557
-    Replaces custom tags as described at the top of this file (language.py).
558
-
559 430
     Unlike `replace_include_tags`, `replace_python_tags`, and `build_tree`, this
560 431
     function should not modify `context`. The argument `source` is passed in as
561 432
     a separate argument to reflect this difference in paradigm.
562
-
563
-    **Parameters:**
564
-        `context`           the context of this request
565
-        `source`            the text of this request
566
-        `source_format`     the format that `source` should be considered to have
567
-    **Optional Parameters:**
568
-        `disable_markdown`  a boolean that disables markdown transformations
569
-    **Returns:**
570
-        `source`            the modified text
571 433
     """
572 434
     custom_tags = context["cs_custom_tags"]
573 435
     names_todo = list(custom_tags.keys())
@@ -695,14 +557,8 @@ def replace_custom_tags(context, source, source_format, disable_markdown=False):
695 557
 
696 558
 def build_tree(context, source):
697 559
     """
698
-    Create a BeautifulSoup tree and implement the functionality of the remainder
699
-    of the custom tags.
700
-
701
-    **Parameters:**
702
-        `context`   the context of this request
703
-        `source`    the text of this request
704
-    **Returns:**
705
-        `source`    the modified text
560
+    Helper to create a BeautifulSoup tree and implement the functionality of
561
+    the remainder of the custom tags.
706 562
     """
707 563
     tree = BeautifulSoup(source, "html.parser")
708 564
 
@@ -847,17 +703,7 @@ def replace_toplevel_element(
847 703
 
848 704
 def parse_tag(tag):
849 705
     """
850
-    Railroad diagram for the syntax of a tag
851
-
852
-                             /<----------+---------------------------------------------------\
853
-                            /             \                                                   \
854
-    `<` -> WS* -> name -> WS -> parameter -+-> WS* -> `=` -> WS* -> value ---------------------+-> WS* -> `>`
855
-                       \                                         \                            /
856
-                        \-> `>`                                   \---> `"` -> .* -> `"` --->/
857
-                                                                   \                        /
858
-                                                                    \-> `'` -> .* -> `'` ->/
859
-    WS = whitespace, * = Kleene star,
860
-    . = any character besides relevant delimiter
706
+    Helper to parse a tag into its components.
861 707
     """
862 708
     # Note that this allows for strange things, like
863 709
     #   <example opt=ion=s>
@@ -928,22 +774,13 @@ def parse_tag(tag):
928 774
 
929 775
 def execute_python(context, body, variables, offset, sourcefile):
930 776
     """
931
-    Evalutes code in a given environment, and returns its output (if any).
777
+    Helper.  Evalutes code in a given environment, and returns its output (if
778
+    any).
932 779
 
933 780
     Makes use of a special variable `cs___WEBOUT`, which is a file-like object.
934 781
     Any data written to `cs___WEBOUT` will be returned. Overwrites `print` in
935 782
     the given environment so that its output is directed to `cs___WEBOUT`
936 783
     instead of STDOUT.
937
-
938
-    **Parameters:**
939
-        `context`       the context of this request
940
-        `body`          the string representation of python code to be executed
941
-        `variables`     the dictionary representation of the environment in
942
-                          which the code should be executed
943
-        `offset`        the adjustment that should be made to line numbering
944
-        `sourcefile`    the name of the file from which this code was taken
945
-    **Returns:**
946
-        `result`        string containing anything written to `cs___WEBOUT`
947 784
     """
948 785
     variables.update({"cs___WEBOUT": StringIO()})
949 786
     try:

+ 1
- 1
website/docs/api/content.py View File

@@ -110,5 +110,5 @@ if not broke:
110 110
             _print()
111 111
 
112 112
 cs_problem_spec = [
113
-    csm_language._md_format_string(globals(), "".join(cs_problem_spec), False)
113
+    csm_language.html_from_source(globals(), "".join(cs_problem_spec), "md")
114 114
 ]

+ 1
- 1
website/docs/preload.py View File

@@ -32,7 +32,7 @@ def callout(note, header, style):
32 32
 </div>""" % (
33 33
         style,
34 34
         header,
35
-        csm_language._md_format_string(globals(), note),
35
+        csm_language.html_from_source(globals(), note, "md"),
36 36
     )
37 37
 
38 38
 

Loading…
Cancel
Save