diff options
author | ptmcg <ptmcg@austin.rr.com> | 2020-10-24 23:34:49 -0500 |
---|---|---|
committer | ptmcg <ptmcg@austin.rr.com> | 2020-10-24 23:34:49 -0500 |
commit | 3c495dbb0fb80cd1600984919a45a5c54baa2806 (patch) | |
tree | b9409cc2461e316e45d2819a190d46e795501105 | |
parent | 22027ba6256c8a12b08379c790875123f6a20e80 (diff) | |
download | pyparsing-git-3c495dbb0fb80cd1600984919a45a5c54baa2806.tar.gz |
ParseResults.List class to support returning an actual list from a parse action, plus aslist and asdict args to Group and Dict classes to emit native Python types instead of ParseResults; also update repr() output of ParseResults to include the type name instead of just a bare tuple.
-rw-r--r-- | CHANGES | 35 | ||||
-rw-r--r-- | examples/jsonParser.py | 64 | ||||
-rw-r--r-- | pyparsing/core.py | 26 | ||||
-rw-r--r-- | pyparsing/results.py | 64 | ||||
-rw-r--r-- | tests/json_parser_tests.py | 400 | ||||
-rw-r--r-- | tests/test_unit.py | 541 |
6 files changed, 568 insertions, 562 deletions
@@ -17,6 +17,30 @@ Version 3.0.0b1 consistent with Python calling conventions. All warnings warned by diagnostic flags have been converted from `SyntaxWarnings` to `UserWarnings`. +- To support parsers that are intended to generate native Python collection + types such as lists and dicts, the `Group` and `Dict` classes now accept an + additional boolean keyword argument `aslist` and `asdict` respectively. See + the `jsonParser.py` example in the `pyparsing/examples` source directory for + how to return types as `ParseResults` and as Python collection types, and the + distinctions in working with the different types. + + In addition parse actions that must return a value of list type (which would + normally be converted internally to a ParseResults) can override this default + behavior by returning their list wrapped in the new `ParseResults.List` class: + + # this parse action tries to return a list, but pyparsing + # will convert to a ParseResults + def return_as_list_but_still_get_parse_results(tokens): + return tokens.asList() + + # this parse action returns the tokens as a list, and pyparsing will + # maintain its list type in the final parsing results + def return_as_list(tokens): + return ParseResults.List(tokens.asList()) + + This is the mechanism used internally by the `Group` class when defined + using `aslist=True`. + - API CHANGE Added `cache_hit` keyword argument to debug actions. Previously, if packrat parsing was enabled, the debug methods were not called in the event of cache @@ -48,6 +72,17 @@ Version 3.0.0b1 has been skipped (was previously inconsistent, reported in Issue #244, by Frank Goyens, thanks!). +- Modified the repr() output for `ParseResults` to include the class + name as part of the output. This is to clarify for new pyparsing users + who misread the repr output as a tuple of a list and a dict. pyparsing + results will now read like: + + ParseResults(['abc', 'def'], {'qty': 100}] + + instead of just: + + (['abc', 'def'], {'qty': 100}] + - Fixed bugs in Each when passed OneOrMore or ZeroOrMore expressions: . first expression match could be enclosed in an extra nesting level . out-of-order expressions now handled correctly if mixed with required diff --git a/examples/jsonParser.py b/examples/jsonParser.py index cf01318..6d6b1c2 100644 --- a/examples/jsonParser.py +++ b/examples/jsonParser.py @@ -41,6 +41,9 @@ def make_keyword(kwd_str, kwd_value): return pp.Keyword(kwd_str).setParseAction(pp.replaceWith(kwd_value))
+# set to False to return ParseResults
+RETURN_PYTHON_COLLECTIONS = True
+
TRUE = make_keyword("true", True)
FALSE = make_keyword("false", False)
NULL = make_keyword("null", None)
@@ -52,14 +55,29 @@ jsonNumber = ppc.number() jsonObject = pp.Forward().setName("jsonObject")
jsonValue = pp.Forward().setName("jsonValue")
+
jsonElements = pp.delimitedList(jsonValue)
-jsonArray = pp.Group(LBRACK + pp.Optional(jsonElements, []) + RBRACK)
-jsonValue << (
- jsonString | jsonNumber | pp.Group(jsonObject) | jsonArray | TRUE | FALSE | NULL
+# jsonArray = pp.Group(LBRACK + pp.Optional(jsonElements, []) + RBRACK)
+# jsonValue << (
+# jsonString | jsonNumber | pp.Group(jsonObject) | jsonArray | TRUE | FALSE | NULL
+# )
+# memberDef = pp.Group(jsonString + COLON + jsonValue).setName("jsonMember")
+
+jsonArray = pp.Group(
+ LBRACK + pp.Optional(jsonElements) + RBRACK, aslist=RETURN_PYTHON_COLLECTIONS
)
-memberDef = pp.Group(jsonString + COLON + jsonValue).setName("jsonMember")
+
+jsonValue << (jsonString | jsonNumber | jsonObject | jsonArray | TRUE | FALSE | NULL)
+
+memberDef = pp.Group(
+ jsonString + COLON + jsonValue, aslist=RETURN_PYTHON_COLLECTIONS
+).setName("jsonMember")
+
jsonMembers = pp.delimitedList(memberDef)
-jsonObject << pp.Dict(LBRACE + pp.Optional(jsonMembers) + RBRACE)
+# jsonObject << pp.Dict(LBRACE + pp.Optional(jsonMembers) + RBRACE)
+jsonObject << pp.Dict(
+ LBRACE + pp.Optional(jsonMembers) + RBRACE, asdict=RETURN_PYTHON_COLLECTIONS
+)
jsonComment = pp.cppStyleComment
jsonObject.ignore(jsonComment)
@@ -72,7 +90,7 @@ if __name__ == "__main__": "title": "example glossary",
"GlossDiv": {
"title": "S",
- "GlossList":
+ "GlossList": [
{
"ID": "SGML",
"SortAs": "SGML",
@@ -91,22 +109,42 @@ if __name__ == "__main__": "EmptyDict" : {},
"EmptyList" : []
}
+ ]
}
}
}
"""
results = jsonObject.parseString(testdata)
+
results.pprint()
+ if RETURN_PYTHON_COLLECTIONS:
+ from pprint import pprint
+
+ pprint(results)
+ else:
+ results.pprint()
print()
def testPrint(x):
print(type(x), repr(x))
- print(list(results.glossary.GlossDiv.GlossList.keys()))
- testPrint(results.glossary.title)
- testPrint(results.glossary.GlossDiv.GlossList.ID)
- testPrint(results.glossary.GlossDiv.GlossList.FalseValue)
- testPrint(results.glossary.GlossDiv.GlossList.Acronym)
- testPrint(results.glossary.GlossDiv.GlossList.EvenPrimesGreaterThan2)
- testPrint(results.glossary.GlossDiv.GlossList.PrimesLessThan10)
+ if RETURN_PYTHON_COLLECTIONS:
+ results = results[0]
+ print(list(results["glossary"]["GlossDiv"]["GlossList"][0].keys()))
+ testPrint(results["glossary"]["title"])
+ testPrint(results["glossary"]["GlossDiv"]["GlossList"][0]["ID"])
+ testPrint(results["glossary"]["GlossDiv"]["GlossList"][0]["FalseValue"])
+ testPrint(results["glossary"]["GlossDiv"]["GlossList"][0]["Acronym"])
+ testPrint(
+ results["glossary"]["GlossDiv"]["GlossList"][0]["EvenPrimesGreaterThan2"]
+ )
+ testPrint(results["glossary"]["GlossDiv"]["GlossList"][0]["PrimesLessThan10"])
+ else:
+ print(list(results.glossary.GlossDiv.GlossList.keys()))
+ testPrint(results.glossary.title)
+ testPrint(results.glossary.GlossDiv.GlossList.ID)
+ testPrint(results.glossary.GlossDiv.GlossList.FalseValue)
+ testPrint(results.glossary.GlossDiv.GlossList.Acronym)
+ testPrint(results.glossary.GlossDiv.GlossList.EvenPrimesGreaterThan2)
+ testPrint(results.glossary.GlossDiv.GlossList.PrimesLessThan10)
diff --git a/pyparsing/core.py b/pyparsing/core.py index bf57c30..c05c93c 100644 --- a/pyparsing/core.py +++ b/pyparsing/core.py @@ -4457,6 +4457,9 @@ class Group(TokenConverter): """Converter to return the matched tokens as a list - useful for returning tokens of :class:`ZeroOrMore` and :class:`OneOrMore` expressions. + The optional ``aslist`` argument when set to True will return the + parsed tokens as a Python list instead of a pyparsing ParseResults. + Example:: ident = Word(alphas) @@ -4471,12 +4474,20 @@ class Group(TokenConverter): # -> ['fn', ['a', 'b', '100']] """ - def __init__(self, expr): + def __init__(self, expr, aslist=False): super().__init__(expr) self.saveAsList = True + self._asPythonList = aslist def postParse(self, instring, loc, tokenlist): - return [tokenlist] + if self._asPythonList: + return ParseResults.List( + tokenlist.asList() + if isinstance(tokenlist, ParseResults) + else list(tokenlist) + ) + else: + return [tokenlist] class Dict(TokenConverter): @@ -4485,6 +4496,9 @@ class Dict(TokenConverter): token in the expression as its key. Useful for tabular report scraping when the first column can be used as a item key. + The optional ``asdict`` argument when set to True will return the + parsed tokens as a Python dict instead of a pyparsing ParseResults. + Example:: data_word = Word(alphas) @@ -4519,9 +4533,10 @@ class Dict(TokenConverter): See more examples at :class:`ParseResults` of accessing fields by results name. """ - def __init__(self, expr): + def __init__(self, expr, asdict=False): super().__init__(expr) self.saveAsList = True + self._asPythonDict = asdict def postParse(self, instring, loc, tokenlist): for i, tok in enumerate(tokenlist): @@ -4544,10 +4559,7 @@ class Dict(TokenConverter): else: tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0], i) - if self.resultsName: - return [tokenlist] - else: - return tokenlist + return tokenlist if not self._asPythonDict else tokenlist.asDict() class Suppress(TokenConverter): diff --git a/pyparsing/results.py b/pyparsing/results.py index cac1a3c..a30e919 100644 --- a/pyparsing/results.py +++ b/pyparsing/results.py @@ -80,6 +80,57 @@ class ParseResults: "__weakref__", ] + class List(list): + """ + Simple wrapper class to distinguish parsed list results that should be preserved + as actual Python lists, instead of being converted to :class:`ParseResults`: + + LBRACK, RBRACK = map(pp.Suppress, "[]") + element = pp.Forward() + item = ppc.integer + element_list = LBRACK + pp.delimitedList(element) + RBRACK + + # add parse actions to convert from ParseResults to actual Python collection types + def as_python_list(t): + return pp.ParseResults.List(t.asList()) + element_list.addParseAction(as_python_list) + + element <<= item | element_list + + element.runTests(''' + 100 + [2,3,4] + [[2, 1],3,4] + [(2, 1),3,4] + (2,3,4) + ''', postParse=lambda s, r: (r[0], type(r[0]))) + + prints: + + 100 + (100, <class 'int'>) + + [2,3,4] + ([2, 3, 4], <class 'list'>) + + [[2, 1],3,4] + ([[2, 1], 3, 4], <class 'list'>) + + (Used internally by :class:`Group` when `aslist=True`.) + """ + + def __new__(cls, contained=None): + if contained is None: + contained = [] + + if not isinstance(contained, list): + raise TypeError( + "{} may only be constructed with a list," + " not {}".format(cls.__name__, type(contained).__name__) + ) + + return list.__new__(cls) + def __new__(cls, toklist=None, name=None, **kwargs): if isinstance(toklist, ParseResults): return toklist @@ -87,11 +138,12 @@ class ParseResults: self._name = None self._parent = None self._all_names = set() + if toklist is None: toklist = [] - if isinstance(toklist, list): - self._toklist = toklist[:] - elif isinstance(toklist, _generator_type): + if isinstance(toklist, ParseResults.List): + self._toklist = [toklist[:]] + elif isinstance(toklist, (list, _generator_type)): self._toklist = list(toklist) else: self._toklist = [toklist] @@ -397,7 +449,7 @@ class ParseResults: return other + self def __repr__(self): - return "({!r}, {})".format(self._toklist, self.asDict()) + return "{}({!r}, {})".format(type(self).__name__, self._toklist, self.asDict()) def __str__(self): return ( @@ -510,7 +562,7 @@ class ParseResults: elif self._parent: par = self._parent() - def lookup(self, sub): + def find_in_parent(self, sub): return next( ( k @@ -521,7 +573,7 @@ class ParseResults: None, ) - return lookup(self) if par else None + return find_in_parent(self) if par else None elif ( len(self) == 1 and len(self._tokdict) == 1 diff --git a/tests/json_parser_tests.py b/tests/json_parser_tests.py index 0b4fc4f..a5779ba 100644 --- a/tests/json_parser_tests.py +++ b/tests/json_parser_tests.py @@ -5,28 +5,29 @@ test1 = """ { - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": [{ - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Markup Language", - "Acronym": "SGML", - "LargestPrimeLessThan100": 97, - "AvogadroNumber": 6.02E23, - "EvenPrimesGreaterThan2": null, - "PrimesLessThan10" : [2,3,5,7], - "WMDsFound" : false, - "IraqAlQaedaConnections" : null, - "Abbrev": "ISO 8879:1986", - "GlossDef": -"A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": ["GML", "XML", "markup"], - "EmptyDict" : {}, - "EmptyList" : [] - }] + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": [ + { + "ID": "SGML", + "SortAs": "SGML", + "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML", "markup"], + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "LargestPrimeLessThan100": 97, + "AvogadroNumber": 6.02E23, + "EvenPrimesGreaterThan2": [], + "PrimesLessThan10" : [2,3,5,7], + "FermatTheoremInMargin" : false, + "MapRequiringFiveColors" : null, + "Abbrev": "ISO 8879:1986", + "EmptyDict" : {}, + "EmptyList" : [] + } + ] } } } @@ -45,6 +46,7 @@ test2 = """ } }} """ + test3 = """ {"widget": { "debug": "on", @@ -67,173 +69,173 @@ test4 = """ { "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", -/* - Defines glossary variables that template designers - can use across the site. You can add new - variables to this set by creating a new init-param, with - the param-name prefixed with "configGlossary:". -*/ + /* + Defines glossary variables that template designers + can use across the site. You can add new + variables to this set by creating a new init-param, with + the param-name prefixed with "configGlossary:". + */ "init-param": { "configGlossary:installationAt": "Philadelphia, PA", "configGlossary:adminEmail": "ksm@pobox.com", "configGlossary:poweredBy": "Cofax", "configGlossary:poweredByIcon": "/images/cofax.gif", "configGlossary:staticPath": "/content/static", -/* - Defines the template loader and template processor - classes. These are implementations of org.cofax.TemplateProcessor - and org.cofax.TemplateLoader respectively. Simply create new - implementation of these classes and set them here if the default - implementations do not suit your needs. Leave these alone - for the defaults. -*/ + /* + Defines the template loader and template processor + classes. These are implementations of org.cofax.TemplateProcessor + and org.cofax.TemplateLoader respectively. Simply create new + implementation of these classes and set them here if the default + implementations do not suit your needs. Leave these alone + for the defaults. + */ "templateProcessorClass": "org.cofax.WysiwygTemplate", "templateLoaderClass": "org.cofax.FilesTemplateLoader", "templatePath": "templates", "templateOverridePath": "", -/* - Defines the names of the default templates to look for - when acquiring WYSIWYG templates. Leave these at their - defaults for most usage. -*/ + /* + Defines the names of the default templates to look for + when acquiring WYSIWYG templates. Leave these at their + defaults for most usage. + */ "defaultListTemplate": "listTemplate.htm", "defaultFileTemplate": "articleTemplate.htm", -/* - New! useJSP switches on JSP template processing. - jspListTemplate and jspFileTemplate are the names - of the default templates to look for when aquiring JSP - templates. Cofax currently in production at KR has useJSP - set to false, since our sites currently use WYSIWYG - templating exclusively. -*/ + /* + New! useJSP switches on JSP template processing. + jspListTemplate and jspFileTemplate are the names + of the default templates to look for when aquiring JSP + templates. Cofax currently in production at KR has useJSP + set to false, since our sites currently use WYSIWYG + templating exclusively. + */ "useJSP": false, "jspListTemplate": "listTemplate.jsp", "jspFileTemplate": "articleTemplate.jsp", -/* - Defines the packageTag cache. This cache keeps - Cofax from needing to interact with the database - to look up packageTag commands. -*/ + /* + Defines the packageTag cache. This cache keeps + Cofax from needing to interact with the database + to look up packageTag commands. + */ "cachePackageTagsTrack": 200, "cachePackageTagsStore": 200, "cachePackageTagsRefresh": 60, -/* - Defines the template cache. Keeps Cofax from needing - to go to the file system to load a raw template from - the file system. -*/ + /* + Defines the template cache. Keeps Cofax from needing + to go to the file system to load a raw template from + the file system. + */ "cacheTemplatesTrack": 100, "cacheTemplatesStore": 50, "cacheTemplatesRefresh": 15, -/* - Defines the page cache. Keeps Cofax from processing - templates to deliver to users. -*/ + /* + Defines the page cache. Keeps Cofax from processing + templates to deliver to users. + */ "cachePagesTrack": 200, "cachePagesStore": 100, "cachePagesRefresh": 10, "cachePagesDirtyRead": 10, -/* - Defines the templates Cofax will use when - being browsed by a search engine identified in - searchEngineRobotsDb -*/ + /* + Defines the templates Cofax will use when + being browsed by a search engine identified in + searchEngineRobotsDb + */ "searchEngineListTemplate": "forSearchEnginesList.htm", "searchEngineFileTemplate": "forSearchEngines.htm", "searchEngineRobotsDb": "WEB-INF/robots.db", -/* - New! useDataStore enables/disables the Cofax database pool -*/ + /* + New! useDataStore enables/disables the Cofax database pool + */ "useDataStore": true, -/* - Defines the implementation of org.cofax.DataStore that Cofax - will use. If this DataStore class does not suit your needs - simply implement a new DataStore class and set here. -*/ + /* + Defines the implementation of org.cofax.DataStore that Cofax + will use. If this DataStore class does not suit your needs + simply implement a new DataStore class and set here. + */ "dataStoreClass": "org.cofax.SqlDataStore", -/* - Defines the implementation of org.cofax.Redirection that - Cofax will use. If this Redirection class does not suit - your needs simply implenet a new Redirection class - and set here. -*/ + /* + Defines the implementation of org.cofax.Redirection that + Cofax will use. If this Redirection class does not suit + your needs simply implenet a new Redirection class + and set here. + */ "redirectionClass": "org.cofax.SqlRedirection", -/* - Defines the data store name. Keep this at the default -*/ + /* + Defines the data store name. Keep this at the default + */ "dataStoreName": "cofax", -/* - Defines the JDBC driver that Cofax's database pool will use -*/ + /* + Defines the JDBC driver that Cofax's database pool will use + */ "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", -/* - Defines the JDBC connection URL to connect to the database -*/ + /* + Defines the JDBC connection URL to connect to the database + */ "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", -/* - Defines the user name to connect to the database -*/ + /* + Defines the user name to connect to the database + */ "dataStoreUser": "sa", -/* - Defines the password to connect to the database -*/ + /* + Defines the password to connect to the database + */ "dataStorePassword": "dataStoreTestQuery", -/* - A query that will run to test the validity of the - connection in the pool. -*/ + /* + A query that will run to test the validity of the + connection in the pool. + */ "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", -/* - A log file to print out database information -*/ + /* + A log file to print out database information + */ "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", -/* - The number of connection to initialize on startup -*/ + /* + The number of connection to initialize on startup + */ "dataStoreInitConns": 10, -/* - The maximum number of connection to use in the pool -*/ + /* + The maximum number of connection to use in the pool + */ "dataStoreMaxConns": 100, -/* - The number of times a connection will be utilized from the - pool before disconnect -*/ + /* + The number of times a connection will be utilized from the + pool before disconnect + */ "dataStoreConnUsageLimit": 100, -/* - The level of information to print to the log -*/ + /* + The level of information to print to the log + */ "dataStoreLogLevel": "debug", -/* - The maximum URL length allowable by the CDS Servlet - Helps to prevent hacking -*/ + /* + The maximum URL length allowable by the CDS Servlet + Helps to prevent hacking + */ "maxUrlLength": 500}}, -/* - Defines the Email Servlet -*/ + /* + Defines the Email Servlet + */ { "servlet-name": "cofaxEmail", "servlet-class": "org.cofax.cds.EmailServlet", "init-param": { -/* - The mail host to be used by the mail servlet -*/ + /* + The mail host to be used by the mail servlet + */ "mailHost": "mail1", -/* - An override -*/ + /* + An override + */ "mailHostOverride": "mail2"}}, -/* - Defines the Admin Servlet - used to refresh cache on - demand and see statistics -*/ + /* + Defines the Admin Servlet - used to refresh cache on + demand and see statistics + */ { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet"}, -/* - Defines the File Servlet - used to display files like Apache -*/ + /* + Defines the File Servlet - used to display files like Apache + */ { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet"}, @@ -241,87 +243,87 @@ test4 = """ "servlet-name": "cofaxTools", "servlet-class": "org.cofax.cms.CofaxToolsServlet", "init-param": { -/* - Path to the template folder relative to the tools tomcat installation. -*/ + /* + Path to the template folder relative to the tools tomcat installation. + */ "templatePath": "toolstemplates/", -/* - Logging boolean 1 = on, 0 = off -*/ + /* + Logging boolean 1 = on, 0 = off + */ "log": 1, -/* - Location of log. If empty, log will be written System.out -*/ + /* + Location of log. If empty, log will be written System.out + */ "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", -/* - Max size of log in BITS. If size is empty, no limit to log. - If size is defined, log will be overwritten upon reaching defined size. -*/ + /* + Max size of log in BITS. If size is empty, no limit to log. + If size is defined, log will be overwritten upon reaching defined size. + */ "logMaxSize": "", -/* - DataStore logging boolean 1 = on, 0 = off -*/ + /* + DataStore logging boolean 1 = on, 0 = off + */ "dataLog": 1, -/* - DataStore location of log. If empty, log will be written System.out -*/ + /* + DataStore location of log. If empty, log will be written System.out + */ "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", -/* - Max size of log in BITS. If size is empty, no limit to log. - If size is defined, log will be overwritten upon reaching defined size. -*/ + /* + Max size of log in BITS. If size is empty, no limit to log. + If size is defined, log will be overwritten upon reaching defined size. + */ "dataLogMaxSize": "", -/* - Http string relative to server root to call for page cache - removal to Cofax Servlet. -*/ + /* + Http string relative to server root to call for page cache + removal to Cofax Servlet. + */ "removePageCache": "/content/admin/remove?cache=pages&id=", -/* - Http string relative to server root to call for template - cache removal to Cofax Servlet. -*/ + /* + Http string relative to server root to call for template + cache removal to Cofax Servlet. + */ "removeTemplateCache": "/content/admin/remove?cache=templates&id=", -/* - Location of folder from root of drive that will be used for - ftp transfer from beta server or user hard drive to live servers. - Note that Edit Article will not function without this variable - set correctly. MultiPart request relies upon access to this folder. -*/ + /* + Location of folder from root of drive that will be used for + ftp transfer from beta server or user hard drive to live servers. + Note that Edit Article will not function without this variable + set correctly. MultiPart request relies upon access to this folder. + */ "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", -/* - Defines whether the Server should look in another path for - config files or variables. -*/ + /* + Defines whether the Server should look in another path for + config files or variables. + */ "lookInContext": 1, -/* - Number of the ID of the top level administration group in tblPermGroups. -*/ + /* + Number of the ID of the top level administration group in tblPermGroups. + */ "adminGroupID": 4, -/* - Is the tools app running on the 'beta server'. -*/ + /* + Is the tools app running on the 'beta server'. + */ "betaServer": true}}], "servlet-mapping": { -/* - URL mapping for the CDS Servlet -*/ + /* + URL mapping for the CDS Servlet + */ "cofaxCDS": "/", -/* - URL mapping for the Email Servlet -*/ + /* + URL mapping for the Email Servlet + */ "cofaxEmail": "/cofaxutil/aemail/*", -/* - URL mapping for the Admin servlet -*/ + /* + URL mapping for the Admin servlet + */ "cofaxAdmin": "/admin/*", -/* - URL mapping for the Files servlet -*/ + /* + URL mapping for the Files servlet + */ "fileServlet": "/static/*", "cofaxTools": "/tools/*"}, -/* - New! The cofax taglib descriptor file -*/ + /* + New! The cofax taglib descriptor file + */ "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} diff --git a/tests/test_unit.py b/tests/test_unit.py index 4897d97..ffec86a 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -400,346 +400,213 @@ class Test2_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase): def testParseJSONData(self): expected = [ - [ - [ - "glossary", - [ - ["title", "example glossary"], - [ - "GlossDiv", - [ - ["title", "S"], - [ - "GlossList", - [ - [ - ["ID", "SGML"], - ["SortAs", "SGML"], - [ - "GlossTerm", - "Standard Generalized Markup Language", - ], - ["Acronym", "SGML"], - ["LargestPrimeLessThan100", 97], - ["AvogadroNumber", 6.02e23], - ["EvenPrimesGreaterThan2", None], - ["PrimesLessThan10", [2, 3, 5, 7]], - ["WMDsFound", False], - ["IraqAlQaedaConnections", None], - ["Abbrev", "ISO 8879:1986"], - [ - "GlossDef", - "A meta-markup language, used to create markup languages such as " - "DocBook.", - ], - ["GlossSeeAlso", ["GML", "XML", "markup"]], - ["EmptyDict", []], - ["EmptyList", [[]]], - ] - ], - ], - ], - ], - ], - ] - ], - [ - [ - "menu", - [ - ["id", "file"], - ["value", "File:"], - [ - "popup", - [ - [ - "menuitem", - [ - [ - ["value", "New"], - ["onclick", "CreateNewDoc()"], - ], - [["value", "Open"], ["onclick", "OpenDoc()"]], - [["value", "Close"], ["onclick", "CloseDoc()"]], - ], - ] - ], - ], - ], - ] - ], - [ - [ - "widget", - [ - ["debug", "on"], - [ - "window", - [ - ["title", "Sample Konfabulator Widget"], - ["name", "main_window"], - ["width", 500], - ["height", 500], - ], - ], - [ - "image", - [ - ["src", "Images/Sun.png"], - ["name", "sun1"], - ["hOffset", 250], - ["vOffset", 250], - ["alignment", "center"], - ], - ], - [ - "text", - [ - ["data", "Click Here"], - ["size", 36], - ["style", "bold"], - ["name", "text1"], - ["hOffset", 250], - ["vOffset", 100], - ["alignment", "center"], - [ - "onMouseUp", - "sun1.opacity = (sun1.opacity / 100) * 90;", - ], - ], - ], - ], - ] - ], - [ - [ - "web-app", - [ - [ - "servlet", - [ - [ - ["servlet-name", "cofaxCDS"], - ["servlet-class", "org.cofax.cds.CDSServlet"], - [ - "init-param", - [ - [ - "configGlossary:installationAt", - "Philadelphia, PA", - ], - [ - "configGlossary:adminEmail", - "ksm@pobox.com", - ], - ["configGlossary:poweredBy", "Cofax"], - [ - "configGlossary:poweredByIcon", - "/images/cofax.gif", - ], - [ - "configGlossary:staticPath", - "/content/static", - ], - [ - "templateProcessorClass", - "org.cofax.WysiwygTemplate", - ], - [ - "templateLoaderClass", - "org.cofax.FilesTemplateLoader", - ], - ["templatePath", "templates"], - ["templateOverridePath", ""], - ["defaultListTemplate", "listTemplate.htm"], - [ - "defaultFileTemplate", - "articleTemplate.htm", - ], - ["useJSP", False], - ["jspListTemplate", "listTemplate.jsp"], - ["jspFileTemplate", "articleTemplate.jsp"], - ["cachePackageTagsTrack", 200], - ["cachePackageTagsStore", 200], - ["cachePackageTagsRefresh", 60], - ["cacheTemplatesTrack", 100], - ["cacheTemplatesStore", 50], - ["cacheTemplatesRefresh", 15], - ["cachePagesTrack", 200], - ["cachePagesStore", 100], - ["cachePagesRefresh", 10], - ["cachePagesDirtyRead", 10], - [ - "searchEngineListTemplate", - "forSearchEnginesList.htm", - ], - [ - "searchEngineFileTemplate", - "forSearchEngines.htm", - ], - [ - "searchEngineRobotsDb", - "WEB-INF/robots.db", - ], - ["useDataStore", True], - [ - "dataStoreClass", - "org.cofax.SqlDataStore", - ], - [ - "redirectionClass", - "org.cofax.SqlRedirection", - ], - ["dataStoreName", "cofax"], - [ - "dataStoreDriver", - "com.microsoft.jdbc.sqlserver.SQLServerDriver", - ], - [ - "dataStoreUrl", - "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", - ], - ["dataStoreUser", "sa"], - ["dataStorePassword", "dataStoreTestQuery"], - [ - "dataStoreTestQuery", - "SET NOCOUNT ON;select test='test';", - ], - [ - "dataStoreLogFile", - "/usr/local/tomcat/logs/datastore.log", - ], - ["dataStoreInitConns", 10], - ["dataStoreMaxConns", 100], - ["dataStoreConnUsageLimit", 100], - ["dataStoreLogLevel", "debug"], - ["maxUrlLength", 500], - ], - ], - ], - [ - ["servlet-name", "cofaxEmail"], - ["servlet-class", "org.cofax.cds.EmailServlet"], - [ - "init-param", - [ - ["mailHost", "mail1"], - ["mailHostOverride", "mail2"], - ], - ], - ], - [ - ["servlet-name", "cofaxAdmin"], - ["servlet-class", "org.cofax.cds.AdminServlet"], - ], - [ - ["servlet-name", "fileServlet"], - ["servlet-class", "org.cofax.cds.FileServlet"], - ], - [ - ["servlet-name", "cofaxTools"], - [ - "servlet-class", - "org.cofax.cms.CofaxToolsServlet", - ], - [ - "init-param", - [ - ["templatePath", "toolstemplates/"], - ["log", 1], - [ - "logLocation", - "/usr/local/tomcat/logs/CofaxTools.log", - ], - ["logMaxSize", ""], - ["dataLog", 1], - [ - "dataLogLocation", - "/usr/local/tomcat/logs/dataLog.log", - ], - ["dataLogMaxSize", ""], - [ - "removePageCache", - "/content/admin/remove?cache=pages&id=", - ], - [ - "removeTemplateCache", - "/content/admin/remove?cache=templates&id=", - ], - [ - "fileTransferFolder", - "/usr/local/tomcat/webapps/content/fileTransferFolder", - ], - ["lookInContext", 1], - ["adminGroupID", 4], - ["betaServer", True], - ], - ], - ], - ], - ], - [ - "servlet-mapping", - [ - ["cofaxCDS", "/"], - ["cofaxEmail", "/cofaxutil/aemail/*"], - ["cofaxAdmin", "/admin/*"], - ["fileServlet", "/static/*"], - ["cofaxTools", "/tools/*"], - ], - ], - [ - "taglib", - [ - ["taglib-uri", "cofax.tld"], - ["taglib-location", "/WEB-INF/tlds/cofax.tld"], - ], + { + "glossary": { + "GlossDiv": { + "GlossList": [ + { + "Abbrev": "ISO 8879:1986", + "Acronym": "SGML", + "AvogadroNumber": 6.02e23, + "EmptyDict": {}, + "EmptyList": [], + "EvenPrimesGreaterThan2": [], + "FermatTheoremInMargin": False, + "GlossDef": "A meta-markup language, " + "used to create markup " + "languages such as " + "DocBook.", + "GlossSeeAlso": ["GML", "XML", "markup"], + "GlossTerm": "Standard Generalized " "Markup Language", + "ID": "SGML", + "LargestPrimeLessThan100": 97, + "MapRequiringFiveColors": None, + "PrimesLessThan10": [2, 3, 5, 7], + "SortAs": "SGML", + } ], + "title": "S", + }, + "title": "example glossary", + } + }, + { + "menu": { + "id": "file", + "popup": { + "menuitem": [ + {"onclick": "CreateNewDoc()", "value": "New"}, + {"onclick": "OpenDoc()", "value": "Open"}, + {"onclick": "CloseDoc()", "value": "Close"}, + ] + }, + "value": "File:", + } + }, + { + "widget": { + "debug": "on", + "image": { + "alignment": "center", + "hOffset": 250, + "name": "sun1", + "src": "Images/Sun.png", + "vOffset": 250, + }, + "text": { + "alignment": "center", + "data": "Click Here", + "hOffset": 250, + "name": "text1", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;", + "size": 36, + "style": "bold", + "vOffset": 100, + }, + "window": { + "height": 500, + "name": "main_window", + "title": "Sample Konfabulator Widget", + "width": 500, + }, + } + }, + { + "web-app": { + "servlet": [ + { + "init-param": { + "cachePackageTagsRefresh": 60, + "cachePackageTagsStore": 200, + "cachePackageTagsTrack": 200, + "cachePagesDirtyRead": 10, + "cachePagesRefresh": 10, + "cachePagesStore": 100, + "cachePagesTrack": 200, + "cacheTemplatesRefresh": 15, + "cacheTemplatesStore": 50, + "cacheTemplatesTrack": 100, + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:installationAt": "Philadelphia, " "PA", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "dataStoreClass": "org.cofax.SqlDataStore", + "dataStoreConnUsageLimit": 100, + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreInitConns": 10, + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreLogLevel": "debug", + "dataStoreMaxConns": 100, + "dataStoreName": "cofax", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT " + "ON;select " + "test='test';", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "defaultFileTemplate": "articleTemplate.htm", + "defaultListTemplate": "listTemplate.htm", + "jspFileTemplate": "articleTemplate.jsp", + "jspListTemplate": "listTemplate.jsp", + "maxUrlLength": 500, + "redirectionClass": "org.cofax.SqlRedirection", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templateOverridePath": "", + "templatePath": "templates", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "useDataStore": True, + "useJSP": False, + }, + "servlet-class": "org.cofax.cds.CDSServlet", + "servlet-name": "cofaxCDS", + }, + { + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2", + }, + "servlet-class": "org.cofax.cds.EmailServlet", + "servlet-name": "cofaxEmail", + }, + { + "servlet-class": "org.cofax.cds.AdminServlet", + "servlet-name": "cofaxAdmin", + }, + { + "servlet-class": "org.cofax.cds.FileServlet", + "servlet-name": "fileServlet", + }, + { + "init-param": { + "adminGroupID": 4, + "betaServer": True, + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "lookInContext": 1, + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "templatePath": "toolstemplates/", + }, + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "servlet-name": "cofaxTools", + }, ], - ] - ], - [ - [ - "menu", - [ - ["header", "SVG Viewer"], - [ - "items", - [ - [["id", "Open"]], - [["id", "OpenNew"], ["label", "Open New"]], - None, - [["id", "ZoomIn"], ["label", "Zoom In"]], - [["id", "ZoomOut"], ["label", "Zoom Out"]], - [["id", "OriginalView"], ["label", "Original View"]], - None, - [["id", "Quality"]], - [["id", "Pause"]], - [["id", "Mute"]], - None, - [["id", "Find"], ["label", "Find..."]], - [["id", "FindAgain"], ["label", "Find Again"]], - [["id", "Copy"]], - [["id", "CopyAgain"], ["label", "Copy Again"]], - [["id", "CopySVG"], ["label", "Copy SVG"]], - [["id", "ViewSVG"], ["label", "View SVG"]], - [["id", "ViewSource"], ["label", "View Source"]], - [["id", "SaveAs"], ["label", "Save As"]], - None, - [["id", "Help"]], - [ - ["id", "About"], - ["label", "About Adobe CVG Viewer..."], - ], - ], - ], + "servlet-mapping": { + "cofaxAdmin": "/admin/*", + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxTools": "/tools/*", + "fileServlet": "/static/*", + }, + "taglib": { + "taglib-location": "/WEB-INF/tlds/cofax.tld", + "taglib-uri": "cofax.tld", + }, + } + }, + { + "menu": { + "header": "SVG Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + None, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + {"id": "OriginalView", "label": "Original View"}, + None, + {"id": "Quality"}, + {"id": "Pause"}, + {"id": "Mute"}, + None, + {"id": "Find", "label": "Find..."}, + {"id": "FindAgain", "label": "Find Again"}, + {"id": "Copy"}, + {"id": "CopyAgain", "label": "Copy Again"}, + {"id": "CopySVG", "label": "Copy SVG"}, + {"id": "ViewSVG", "label": "View SVG"}, + {"id": "ViewSource", "label": "View Source"}, + {"id": "SaveAs", "label": "Save As"}, + None, + {"id": "Help"}, + {"id": "About", "label": "About Adobe CVG Viewer..."}, ], - ] - ], + } + }, ] - for t, exp in zip((test1, test2, test3, test4, test5), expected): - self.assertParseAndCheckList(jsonObject, t, exp, verbose=True) + for t, exp_result in zip((test1, test2, test3, test4, test5), expected): + result = jsonObject.parseString(t) + self.assertEqual(exp_result, result[0]) def testParseCommaSeparatedValues(self): testData = [ @@ -7707,7 +7574,7 @@ class Test2_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase): print(res.asDict()) self.assertEqual( - "(['test', 'blub'], {'word': 'blub'})", + "ParseResults(['test', 'blub'], {'word': 'blub'})", repr(res), "incorrect repr for ParseResults with listAllMatches=False", ) @@ -7720,7 +7587,7 @@ class Test2_WithoutPackrat(ppt.TestParseResultsAsserts, TestCase): print(res.asDict()) self.assertEqual( - "(['test', 'blub'], {'word': ['test', 'blub']})", + "ParseResults(['test', 'blub'], {'word': ['test', 'blub']})", repr(res), "incorrect repr for ParseResults with listAllMatches=True", ) |