File: writeErrorTable.py

package info (click to toggle)
libsbml 5.20.5%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 117,108 kB
  • sloc: cpp: 469,781; xml: 364,270; ansic: 54,078; python: 12,540; makefile: 9,759; sh: 9,245; cs: 8,586; java: 8,151; perl: 6,133; ruby: 4,760; javascript: 1,605; php: 202; csh: 3
file content (821 lines) | stat: -rwxr-xr-x 27,760 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
#!/usr/bin/env python3
# =============================================================================
# @file   writeErrorTable.py
# @brief  Write documentation for SBML error codes
# @author Sarah Keating
# @author Michael Hucka
# =============================================================================
#
# This Python program interrogates the currently-installed libSBML library to
# generate documentation about the libSBML diagnostic codes, and generate
# output used in several contexts.  The output produced by this program is
# controlled by the first command-line argument given to it:
#
# * Given --doc, it will generate a table suitable for use in the libSBML
#   API documentation as it appears in the header of SBMLError.h.  The file
#   it produces is meant to replace src/sbml/common/common-sbmlerror-codes.h.
#   In other words, do the following:
#
#    writeErrorTable.py --doc ../../../src/sbml/common/common-sbmlerror-codes.h
#
# * Given --enum, it will generate the SBMLErrorCode_t enum for SBMLError.h.
#   (The output needs to be manually copied and pasted into SBMLError.h.)
#
# * Given --web, it will generate output used in the table in the web page at
#   http://sbml.org/Facilities/Documentation/Error_Categories.
#
# Here is a typical way of using this program:
#
# 1) Make any desired updates to the text of the diagnostic messages in
#    src/sbml/SBMLErrorTable.h.
#
# 2) Rebuild and install libSBML on your computer, with the Python bindings
#    enabled.  This is necessary because this program (writeErrorTable.py)
#    uses the libSBML Python bindings to do its work.
#
# 3) Configure your PYTHONPATH environment variable to encompass this newly-
#    installed copy of libSBML.  Double-check that your Python executable
#    is in fact picking up the copy of libSBML you think it is.
#
# 4) Run this program 3 times, as following
#
#      ./writeErrorTable.py --doc  doc-fragment.txt
#      ./writeErrorTable.py --enum enum.txt
#      ./writeErrorTable.py --web  sbmlerror-table.html
#
# 5) Replace sbmlerror-table.html on sbml.org, and edit SBMLError.h to
#    insert the contents of doc-fragment.txt and enum.txt in the appropriate
#    places.
#
#<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# This file is part of libSBML.  Please visit http://sbml.org for more
# information about SBML, and the latest version of libSBML.
#
# Copyright (C) 2013-2018 jointly by the following organizations:
#     1. California Institute of Technology, Pasadena, CA, USA
#     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
#     3. University of Heidelberg, Heidelberg, Germany
#
# Copyright (C) 2009-2013 jointly by the following organizations:
#     1. California Institute of Technology, Pasadena, CA, USA
#     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
#
# Copyright (C) 2006-2008 by the California Institute of Technology,
#     Pasadena, CA, USA
#
# Copyright (C) 2002-2005 jointly by the following organizations:
#     1. California Institute of Technology, Pasadena, CA, USA
#     2. Japan Science and Technology Agency, Japan
#
# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation.  A copy of the license agreement is provided
# in the file named "LICENSE.txt" included with this software distribution
# and also available online as http://sbml.org/software/libsbml/license.html
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->

import re, os, sys

from types import *
from imp import *
from string import *
from libsbml import *


# -----------------------------------------------------------------------------
# Globals.
# -----------------------------------------------------------------------------

# The currently-known SBML Levels and Versions.

sbml_levels_versions = [[1, 1], [1, 2],
                        [2, 1], [2, 2], [2, 3], [2, 4],
                        [3, 1], [3, 2]]

# Set of error codes that we ignore for purposes of documentation.

ignored_error_codes = {9999, 10599, 20905, 21112, 29999, 90000, 90501, 99502,
                       99503, 99504, 99994, 99995, 99999}

# Package error code ranges.  The numbers are the low end start values.

package_codes = [['comp',   1000000],
                 ['fbc',    2000000],
                 ['qual',   3000000],
                 ['groups', 4000000],
                 ['layout', 6000000]]

# Our approach to finding error codes starts with numbers and then rummages
# through the list of symbols in the libSBML Python module to find the symbol
# that corresponds to each number.  That works well enough for our purposes
# when the number is above 100, but for lower numbers, we have collisions
# because there are many short enumerations in libSBML.  We don't care about
# numbers lower than 10000 when doing --enum or --doc, but we do for --web,
# where we try to list all error codes.  All of those enumerations turn into
# the same numbers starting from 0, so if you grab a symbol from the Python
# module and examine its value and find that it, say, "3", you can't tell
# which enumeration it came from.  You can only do that if you know the
# symbol names you care about ahead of time.  Since we don't want to
# hard-code all possible libSBML error codes here (avoiding that scenario is
# the point of this program!), we have to find some other way to distinguish
# the cases.  The approach taken here is to only worry about the numbers
# below 1000 using a table that enumerates the symbols we care about; when we
# iterate over all error numbers, we always explicitly map the numbers to the
# following codes.

low_numbered_errors = { 0   : 'XMLUnknownError',
                        1   : 'XMLOutOfMemory',
                        2   : 'XMLFileUnreadable',
                        3   : 'XMLFileUnwritable',
                        4   : 'XMLFileOperationError',
                        5   : 'XMLNetworkAccessError',
                        101 : 'InternalXMLParserError',
                        102 : 'UnrecognizedXMLParserCode',
                        103 : 'XMLTranscoderError' }

# Fragments for the table used in the documentation for SBMLError.h.

doc_table_start_fragment = '''/**
 * @class doc_sbml_error_table
 *
 * @par
<table id="sbmlerror-table"
       class="text-table small-font alt-row-colors"
       width="95%" cellspacing="1" cellpadding="2" border="0">
 <tr style="background: lightgray" class="normal-font">
     <th valign="bottom"><strong>Enumerator</strong></th>
     <th valign="bottom"><strong>Meaning</strong></th>
     <th align="center" width="10">L1 V1</th>
     <th align="center" width="10">L1 V2</th>
     <th align="center" width="10">L2 V1</th>
     <th align="center" width="10">L2 V2</th>
     <th align="center" width="10">L2 V3</th>
     <th align="center" width="10">L2 V4</th>
     <th align="center" width="10">L3 V1</th>
     <th align="center" width="10">L3 V2</th>
 </tr>
'''

doc_table_end_fragment = '''</table>

*/
'''

# Templates and fragments used for the enum.

enum_start_fragment = '''/**
 * @enum SBMLErrorCode_t
 * Codes for all SBML-level errors and warnings from the core specification.
 *
 * @copydetails doc_sbml_error_code_ranges
 */
typedef enum
{
'''

enum_end_fragment = '''} SBMLErrorCode_t;
'''


# Templates and fragments used for the web page.

web_css_fragment = '''<style type='text/css'>
/*<![CDATA[*/
table {
  margin-bottom: 2em;
  border: 1px solid #ccc;
  border-spacing: 0;
}

td, th {
  border: none;
}

th {
  border-bottom: 1px solid #ccc;
}

td {
  border-bottom: 1px solid white;
}

tr:last-child > td {
  border-bottom: none;
}

td.s-fatal {
  font-size: 7pt;
  color: white;
  background-color: darkred;
  font-weight: bold;
  text-align: center;
  letter-spacing: -1px;
  padding: 2px;
}

td.s-fatal:before {
  content: "F";
}

td.s-error {
  font-size: 7pt;
  background-color: #dc143c;
  font-weight: bold;
  text-align: center;
  letter-spacing: -1px;
  padding: 2px;
}

td.s-error:before {
  content: "E";
}

td.s-warning {
  font-size: 7pt;
  background-color: gold;
  font-weight: bold;
  text-align: center;
  letter-spacing: -1px;
  padding: 2px;
}

td.s-warning:before {
  content: "W";
}

td.s-na {
  font-size: 7pt;
  font-weight: bold;
  text-align: center;
  letter-spacing: -1px;
  padding: 2px;
}

td.s-na:before {
  content: "NA";
}

tr.headers {
  background: lightgray;
}

th.levels {
  text-align: center;
  width: 1%;
  padding: 2px;
  letter-spacing: -1px;
}

th.errorid {
  width: 12ex;
  vertical-align: bottom;
}

th.errorid strong {
  color: #333;
}

td.code {
  font-size: 8pt;
  font-family: -webkit-monospace, monospace;
  color: #000;
  text-align: center;
  letter-spacing: -1px;
}

th.errorname {
  width: 20%;
}

td.errorname {
  font-size: 8pt;
  font-family: -webkit-monospace, monospace;
  color: #000;
  height: 26px;   /* to equalize heights of all table rows. */
}

th.meaning {
  font-size: 8pt;
  font-style: italic;
  vertical-align: bottom;
  letter-spacing: -1px;
}

th.meaning strong {
  color: #333;
}

td.meaning code {
  font-size: 8pt;
}

.s-warning
{
  background-color: gold;
  padding: .1em .5em;
  color: black;
}

.s-error
{
  background-color: #D23D24;
  padding: .1em .5em;
  color: white;
}

.s-fatal
{
  background-color: darkred;
  padding: .1em .5em;
  color: white;
}

.table-pkg-separator {
  text-align: center;
  background-color: #ffefd5;
  font-style: italic;
  font-weight: bold;
  line-height: 160%;
}
/*]]>*/
</style>
'''

web_toc_start_fragment = '''
<table style='margin-top: 2pt' id="toc" class="toc" summary="Contents">
<tr><td>
<div style="text-align:left">
<a name="TOC"/></a>
'''

web_toc_end_fragment = '''</div>
</td></tr>
</table>
'''

page_intro_template = '''
<p>This page lists all error, warning and informational diagnostic codes
and messages produced by <a
href="http://sbml.org/Software/libSBML">libSBML</a>, the software
underlying the <a
href="http://sbml.org/Facilities/Validator">Online SBML Validator</a>.
The diagnostics whose code numbers are
between 10000 and 90000 are defined in the <a
href="http://sbml.org/Documents/Specifications">SBML specification documents</a>;
the ones with numbers below 10000 and above 90000 are specific to libSBML or
this validation system and are not defined by the SBML specifications.  The
groups below provide finer-grained divisions in the diagnostics, for better
comprehensibility.</p>

<p>
In the tables below, the right-hand columns titled "L1V1", "L1V2", etc. refer
to Levels and Versions of the SBML specifications, and the entries in
each column refer to the severity of the condition in that
particular Level and Version of SBML.  The codes have the following
meanings:</p>
<p>
<table class="normal-font borderless-table gray-border sm-padding"
       width="200px" cellspacing="1" cellpadding="2" border="0"
       style="margin-left: 2em">
  <tr><th style="text-align: center">Symbol</th><th style="text-align: left">Meaning</th></tr>
  <tr><td class="s-warning gray-border"></td><td>Warning</td></tr>
  <tr><td class="s-error"></td><td>Error</td></tr>
  <tr><td class="s-fatal"></td><td>Fatal</td></tr>
  <tr><td class="s-na" style="border: none"></td><td>Not applicable</td></tr>
</table>
<p>For more information about the meanings of diagnostics numbered between
10000 and 90000, please consult the <a
href="http://sbml.org/Documents/Specifications">SBML specification documents</a>
relevant to the particularl Level and Version combination in question.</p>
'''

web_error_table_start_fragment = '''<center>
<table class="sm-padding sm-font alt-row-colors"
       width="100%" cellspacing="1" cellpadding="2" border="0">
  <tr class="headers">
    <th class="errorid">Error code</th>
    <th class="errorname">LibSBML name</th>
    <th class="errormeaning">Meaning</th>
    <th class="levels">L1 V1</th>
    <th class="levels">L1 V2</th>
    <th class="levels">L2 V1</th>
    <th class="levels">L2 V2</th>
    <th class="levels">L2 V3</th>
    <th class="levels">L2 V4</th>
    <th class="levels">L3 V1</th>
    <th class="levels">L3 V2</th>
  </tr>
'''

web_error_table_end_fragment = '''</table>
</center>
'''

error_groups = [["Internal",                       # HTML anchor
                 [LIBSBML_CAT_INTERNAL],           # category code(s)
                 "Internal errors",                # section title
'''This group of errors concerns problems involving the libSBML software
itself, or the underlying XML parser being used.  This almost certainly
indicates a software defect (i.e., bug) in libSBML.  <strong>Please
<a href='https://sourceforge.net/p/sbml/libsbml/new/'>report</a>
occurrences of these errors to the libSBML developers.</strong>'''
                 ],                                # section intro text

                ["OS",
                 [LIBSBML_CAT_SYSTEM],
                 "Operating system errors",
'''This group of diagnostic codes refers to problems that can be reported
by the operating system where libSBML is running.  This indicates something
that is not a libSBML error, but rather, is outside of the control of libSBML.'''
                 ],

                ["XML",
                 [LIBSBML_CAT_XML],
                 "XML errors",
'''This group of diagnostic codes refers to problems with the XML content of
the SBML file or data stream.  This usually arises from malformed XML, and is
detected at a very low level in the XML parsing system.  Further interpretation
of SBML is aborted if this type of error is encountered.'''
                 ],

                ["Overall-sbml",
                 [LIBSBML_CAT_SBML],
                 "Overall SBML conformance issues",
'''This group of diagnostic codes is concerned with general and overall issues
of conformance to the relevant SBML specifications.'''
                 ],

                ["General",
                 [LIBSBML_CAT_GENERAL_CONSISTENCY],
                 "General SBML consistency issues",
'''This group of diagnostic codes concerns the correct use of SBML constructs
according to the relevant SBML specifications.'''
                 ],

                ["Identifier",
                 [LIBSBML_CAT_IDENTIFIER_CONSISTENCY],
                 "SBML identifier consistency issues",
'''This group of diagnostic codes concerns general SBML issues related to
the identifiers used in SBML documents.  For convenience and greater control
during validation procedures, they are separated from the set of diagnostics 
for general SBML consistency.'''
                 ],

                ["Units",
                 [LIBSBML_CAT_UNITS_CONSISTENCY],
                 "Units consistency issues",
'''This group of diagnostic codes is concerned with the consistency of units
of measurement associated with quantities specified in an SBML model.  For
convenience and greater control during validation procedures, they are 
separated from the set of diagnostics for general SBML consistency.'''
                 ],

                ["MathML",
                 [LIBSBML_CAT_MATHML_CONSISTENCY],
                 "MathML issues",
'''This group of diagnostic codes concerns possible problems that can arise
with the MathML constructs used in an SBML document.'''
                 ],

                ["SBO",
                 [LIBSBML_CAT_SBO_CONSISTENCY],
                 "Systems Biology Ontology (SBO) issues",
'''This group of diagnostic codes concerns possible problems that can arise
with the MathML constructs used in an SBML document.  For
convenience and greater control during validation procedures, they are 
separated from the set of diagnostics for general SBML consistency.'''
                 ],

# The errors previously categorized as LIBSBML_CAT_OVERDETERMINED_MODEL in
# libSBML now seem to be categorized as general SBML spec consistency errors.
#
#                 ["Over",
#                  [LIBSBML_CAT_OVERDETERMINED_MODEL],
#                  "Overdetermined models",
# '''This group of diagnostic codes is concerned with problems encountered
# while testing for overdetermined models; that is, a model where the system is
# overdetermined, therefore violating a tenet of proper SBML.'''
#                  ],

                ["Practice",
                 [LIBSBML_CAT_MODELING_PRACTICE],
                 "Modeling practice issues",
'''This group of diagnostic codes is concerned with matters of good modeling
practices involving SBML and computational modeling.  These are tests
performed by libSBML and do not have equivalent SBML validation rules.
For convenience and greater control during validation procedures, they are
separated from the set of diagnostics for general SBML consistency.'''
                 ],

                ["Internal-consistency",
                 [LIBSBML_CAT_INTERNAL_CONSISTENCY],
                 "Internal SBML consistency issues",
'''This set of diagnostics is concerned with problems that can occur while
validating the libSBML internal representation of SBML constructs. These
are tests performed by libSBML and do not have equivalent SBML validation
rules.  For convenience and greater control during validation procedures, they
are separated from the set of diagnostics for general SBML consistency.'''
                 ],

# 2014-05-14: We decided not to show the conversion codes in the online table.
#
#                 ["conversion",
#                  [LIBSBML_CAT_SBML_L1_COMPAT,
#                   LIBSBML_CAT_SBML_L2V1_COMPAT,
#                   LIBSBML_CAT_SBML_L2V1_COMPAT,
#                   LIBSBML_CAT_SBML_L2V2_COMPAT,
#                   LIBSBML_CAT_SBML_L2V3_COMPAT,
#                   LIBSBML_CAT_SBML_L2V4_COMPAT,
#                   LIBSBML_CAT_SBML_L3V1_COMPAT],
#                  "Problems in converting models",
# '''This group of diagnostic codes is concerned with problems encountered
# during conversion of an SBML document from one SBML Level+Version combination
# to another.'''
#                 ],

]

# -----------------------------------------------------------------------------
# Main code for --doc.
# -----------------------------------------------------------------------------

def write_doc(stream, module):
  stream.write(doc_table_start_fragment)
  for errNum in sorted(get_numeric_constants(module) - ignored_error_codes):
    stream.write(make_doc_row_text(errNum, module))
  stream.write(doc_table_end_fragment)


def make_doc_row_text(errNum, module):
  s = get_symbol(module, errNum)
  output  = '<tr>'
  output += '<td class="code">@sbmlconstant{' + s + ', SBMLErrorCode_t}</td>\n'
  if errNum < 99999:
    e = SBMLError(errNum, 1, 1)
    if not e.isValid():
      return ''
    output += '<td class="meaning">{}</td>\n'.format(to_html(e.getShortMessage()))
    for lv in sbml_levels_versions:
      e = SBMLError(errNum, lv[0], lv[1])
      output += '<td class="{}"></td>\n'.format(get_severity_class(e.getSeverity()))
  else:
    for package in package_codes:
      pkg_name  = package[0]
      pkg_start = package[1]
      pkg_end   = pkg_start + 1000000
      if errNum > pkg_start and errNum < pkg_end:
        # Packages are only possible in SBML Level 3.
        # Check existence in some L/V combination.
        elist = []
        for lv in sbml_levels_versions:
          if lv[0] < 3:
            continue
          elist.append(SBMLError(errNum, lv[0], lv[1], '', 0, 0, 0, 0, pkg_name, 1))
        if all(not e.isValid() for e in elist):
          return ''
        first_e = next(e for e in elist if e.isValid())
        output += '<td class="meaning">{}</td>\n'.format(to_html(first_e.getShortMessage()))
        for lv in sbml_levels_versions:
          if lv[0] < 3:
            output += '<td class="{}"></td>\n'.format(get_severity_class(0))
            continue
          e = SBMLError(errNum, lv[0], lv[1], '', 0, 0, 0, 0, pkg_name, 1)
          output += '<td class="{}"></td>\n'.format(get_severity_class(e.getSeverity()))
        break

  output += '</tr>\n'
  print_progress()
  return output


# -----------------------------------------------------------------------------
# Main code for --web.
# -----------------------------------------------------------------------------

def write_web(stream, module):
  stream.write(web_css_fragment)
  stream.write(make_web_toc())
  stream.write(page_intro_template.format(LIBSBML_DOTTED_VERSION))
  for entry in error_groups:
    anchor     = entry[0]
    categories = entry[1]
    title      = entry[2]
    intro      = entry[3]

    stream.write('<a name="{}"><h3>{}</h3></a>\n'.format(anchor, title))
    stream.write('<p>' + intro + '</p>\n')
    stream.write(web_error_table_start_fragment)
    for errNum in sorted(get_numeric_constants(module, 0, 100000) - ignored_error_codes):
      e = SBMLError(errNum, 1, 1)
      if not e.isValid():
        continue
      if not e.getCategory() in categories:
        continue
      stream.write(make_web_row_text(errNum, module))

    for package in package_codes:
      pkg_name  = package[0]
      pkg_start = package[1]
      pkg_end   = pkg_start + 1000000
      printed_separator = False
      for errNum in sorted(get_numeric_constants(module, pkg_start, pkg_end)):
        e = SBMLError(errNum, 3, 1, '', 0, 0, 0, 0, pkg_name, 1)
        msg = e.getShortMessage()
        if not msg:
          continue
        if not e.getCategory() in categories:
          continue
        if not printed_separator:
          stream.write(make_web_table_pkg_separator(pkg_name))
          printed_separator = True
        stream.write(make_web_row_pkg_text(e, errNum, pkg_name, pkg_start, module))

    print_progress("Writing '" + title + "' \n")
    stream.write(web_error_table_end_fragment)


def make_web_toc():
  output = web_toc_start_fragment
  for entry in error_groups:
    anchor  = entry[0]
    title   = entry[2]
    output += '<a href="#{}">{}</a><br/>\n'.format(anchor, title)
  output += web_toc_end_fragment
  return output


def make_web_table_pkg_separator(pkg_name):
  return '<tr><td colspan="' + str(3 + len(sbml_levels_versions)) \
    + '" class="table-pkg-separator">Codes for SBML Level 3 package "' \
    + pkg_name + '"</td></tr>'


def make_web_row_text(errNum, module):
  e = SBMLError(errNum, 1, 1)
  if not e.isValid():
    return ''

  output  = '<tr>'
  output += '<td class="code">{0:05d}</td>'.format(errNum)
  output += '<td class="errorname">{}</td>'.format(get_symbol(module, errNum))
  output += '<td class="meaning">{}</td>'.format(to_html(e.getShortMessage()))
  for lv in sbml_levels_versions:
    e = SBMLError(errNum, lv[0], lv[1])
    severity = e.getSeverity()
    output += '<td class="{}"></td>'.format(get_severity_class(severity))
  output += '</tr>\n'
  return output


def make_web_row_pkg_text(err, errNum, pkg_name, pkg_start, module):
  output  = '<tr>'
  output += '<td class="code">{0}-{1:05d}</td>'.format(pkg_name, errNum-pkg_start)
  output += '<td class="errorname">{}</td>'.format(get_symbol(module, errNum))
  output += '<td class="meaning">{}</td>'.format(to_html(err.getShortMessage()))
  for lv in range(0, len(sbml_levels_versions) - 1):
    output += '<td class="{}"></td>'.format(get_severity_class(0))
  output += '<td class="{}"></td>'.format(get_severity_class(err.getSeverity()))
  output += '</tr>\n'
  return output


# -----------------------------------------------------------------------------
# Main code for --enum.
# -----------------------------------------------------------------------------

def write_enum(stream, module):
  stream.write(enum_start_fragment)
  for errNum in sorted(get_numeric_constants(module, 0, 100000)):
    e = SBMLError(errNum, 1, 1)
    if e.isValid():
      if errNum == 10000:
        stream.write("  ")
      else:
        stream.write(", ")
      stream.write("%s = %s" %(ljust(get_symbol(module, errNum), 37), str(errNum)))
      msg = e.getShortMessage()
      if msg != '':
        if msg[-1] != '.':
          maybe_period = '.'
        else:
          maybe_period = ''
        stream.write(" /*!< " + to_html(msg) + maybe_period + " */")
      stream.write("\n")
      print_progress()

  stream.write(enum_end_fragment)


# -----------------------------------------------------------------------------
# Helper functions.
# -----------------------------------------------------------------------------

def get_module():
  fp, pathname, description = find_module("libsbml")
  return load_module('_libsbml', fp, pathname, description)


def get_numeric_constants(module, low=0, high=90000000):
  constants = set()
  for symbol in dir(module):
    try:                                # Some symbols don't have a value,
      value = eval(symbol)              # so we have to guard against that.
      if isinstance(value, int) and low <= value and value <= high:
          constants.add(value)
    except:
      continue
  return constants


def get_symbol(module, number):
  # This is an inefficient way of getting the string corresponding to a
  # given error number, but we don't care because this application isn't
  # performance-critical.  The algorithm is also fragile, but if we're
  # careful about the range of values attempted (i.e., numbers from 10000
  # to 100000), it should be okay.  Unfortunately, values below 100 hits
  # a number of small enumerations in libSBML, so we have to special-case
  # that.  Really, this whole thing is just awful.
  #
  if number in low_numbered_errors.keys():
    return low_numbered_errors[number]

  symbols = dir(module)
  attributes = [None]*len(symbols)
  for i in range(0, len(symbols)):
    attributes[i] = getattr(module, symbols[i])
  for i in range(0, len(symbols)):
    if isinstance(attributes[i], int) == True and attributes[i] == number:
      return symbols[i]


def get_severity_class(severity):
  if   (severity == 3): return "s-fatal"
  elif (severity == 2): return "s-error"
  elif (severity == 1): return "s-warning"
  else:                 return "s-na"


def to_html(text):
  text = text.replace('<', '&lt;')
  text = text.replace('>', '&gt;')
  text = text.replace('&lt;', '<code>&lt;')
  text = text.replace('&gt;', '&gt;</code>')
  return text


def print_progress(marker="."):
  # Echo to terminal to show progress.
  sys.stdout.write(marker)
  sys.stdout.flush()


# -----------------------------------------------------------------------------
# Main.
# -----------------------------------------------------------------------------

def main (args):
  """Usage:
            writeErrorTable.py [--doc | --enum | --web]  OUTPUTFILE

  The first argument must one of --doc, --enum or --web.

  * Given --doc, it will generate a table suitable for use in the libSBML
    API documentation as it appears in the header of SBMLError.h.

  * Given --enum, it will generate the SBMLErrorCode_t enum for SBMLError.h.

  * Given --web, it will generate output used in the table in the web page at
    http://sbml.org/Facilities/Documentation/Error_Categories.

  It will write its output to OUTPUTFILE.  Note that the name of the file
  will be used as given; no file name extension will be added.  Callers are
  assumed to supply the full name desired.
  """

  # Start with some sanity-checking.

  if len(args) != 3 or (args[1] not in ['--doc', '--enum', '--web']):
    print(main.__doc__)
    sys.exit(1)

  # OK, let's do this thing.

  module = get_module()

  print('Writing output to ' + args[2])
  stream = open(args[2], 'w')

  if args[1] == '--doc':
    write_doc(stream, module)
  elif args[1] == '--enum':
    write_enum(stream, module)
  else:
    write_web(stream, module)

  stream.close()
  print 'Done.'



if __name__ == '__main__':
  main(sys.argv)