feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
## Development
After forking the pyparsing repo, and cloning your fork locally, install the libraries needed to run tests
pip install -Ur tests/requirements.txt
pre-commit install
Run the simple unit tests to ensure your environment is setup
python tests/test_simple_unit.py
Use `tox` to run the full test suite on all supported Python versions
# run a specific test environment
tox -e py312
# run all test environments
tox
# run all test environments in parallel
tox -p
To run `mypy` standalone
tox -e mypy-check

View File

@@ -0,0 +1,61 @@
<style>
.railroad-heading {
font-family: monospace;
}
</style>
<div class="railroad-group">
<h1 class="railroad-heading"></h1>
<div class="railroad-description"></div>
<div class="railroad-svg">
<svg class="railroad-diagram" height="94" viewBox="0 0 529.5 94" width="529.5" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(.5 .5)">
<style>/* <![CDATA[ */
svg.railroad-diagram {
background-color:hsl(30,20%,95%);
}
svg.railroad-diagram path {
stroke-width:3;
stroke:black;
fill:rgba(0,0,0,0);
}
svg.railroad-diagram text {
font:bold 14px monospace;
text-anchor:middle;
}
svg.railroad-diagram text.label{
text-anchor:start;
}
svg.railroad-diagram text.comment{
font:italic 12px monospace;
}
svg.railroad-diagram rect{
stroke-width:3;
stroke:black;
fill:hsl(120,100%,90%);
}
svg.railroad-diagram rect.group-box {
stroke: gray;
stroke-dasharray: 10 5;
fill: none;
}
/* ]]> */
</style><g>
<path d="M20 45v20m10 -20v20m-10 -10h20"></path></g><path d="M40 55h10"></path><g>
<path d="M50 55h0.0"></path><path d="M479.5 55h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="429.5" x="50.0" y="36"></rect><g>
<path d="M50.0 55h10.0"></path><path d="M469.5 55h10.0"></path><g class="terminal ">
<path d="M60.0 55h0.0"></path><path d="M139.5 55h0.0"></path><rect height="22" rx="10" ry="10" width="79.5" x="60.0" y="44"></rect><text x="99.75" y="59">W:(0-9)</text></g><path d="M139.5 55h10"></path><path d="M149.5 55h10"></path><g class="terminal ">
<path d="M159.5 55h0.0"></path><path d="M205.0 55h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="159.5" y="44"></rect><text x="182.25" y="59">':'</text></g><path d="M205.0 55h10"></path><path d="M215.0 55h10"></path><g class="terminal ">
<path d="M225.0 55h0.0"></path><path d="M304.5 55h0.0"></path><rect height="22" rx="10" ry="10" width="79.5" x="225.0" y="44"></rect><text x="264.75" y="59">W:(0-9)</text></g><path d="M304.5 55h10"></path><path d="M314.5 55h10"></path><g class="terminal ">
<path d="M324.5 55h0.0"></path><path d="M370.0 55h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="324.5" y="44"></rect><text x="347.25" y="59">':'</text></g><path d="M370.0 55h10"></path><path d="M380.0 55h10"></path><g class="terminal ">
<path d="M390.0 55h0.0"></path><path d="M469.5 55h0.0"></path><rect height="22" rx="10" ry="10" width="79.5" x="390.0" y="44"></rect><text x="429.75" y="59">W:(0-9)</text></g></g><g class="non-terminal ">
<path d="M50.0 28h0.0"></path><path d="M123.0 28h0.0"></path><text class="comment" x="86.5" y="33">&#91;combine&#93;</text></g></g><path d="M479.5 55h10"></path><path d="M 489.5 55 h 20 m -10 -10 v 20 m 10 -20 v 20"></path></g></svg>
</div>
</div>

View File

@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<style>
.railroad-heading {
font-family: monospace;
}
</style>
</head>
<body>
<div class="railroad-group">
<h1 class="railroad-heading"></h1>
<div class="railroad-description"></div>
<div class="railroad-svg">
<svg class="railroad-diagram" height="94" viewBox="0 0 529.5 94" width="529.5" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(.5 .5)">
<style>/* <![CDATA[ */
svg.railroad-diagram {
background-color:hsl(30,20%,95%);
}
svg.railroad-diagram path {
stroke-width:3;
stroke:black;
fill:rgba(0,0,0,0);
}
svg.railroad-diagram text {
font:bold 14px monospace;
text-anchor:middle;
}
svg.railroad-diagram text.label{
text-anchor:start;
}
svg.railroad-diagram text.comment{
font:italic 12px monospace;
}
svg.railroad-diagram rect{
stroke-width:3;
stroke:black;
fill:hsl(120,100%,90%);
}
svg.railroad-diagram rect.group-box {
stroke: gray;
stroke-dasharray: 10 5;
fill: none;
}
/* ]]> */
</style><g>
<path d="M20 45v20m10 -20v20m-10 -10h20"></path></g><path d="M40 55h10"></path><g>
<path d="M50 55h0.0"></path><path d="M479.5 55h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="429.5" x="50.0" y="36"></rect><g>
<path d="M50.0 55h10.0"></path><path d="M469.5 55h10.0"></path><g class="terminal ">
<path d="M60.0 55h0.0"></path><path d="M139.5 55h0.0"></path><rect height="22" rx="10" ry="10" width="79.5" x="60.0" y="44"></rect><text x="99.75" y="59">W:(0-9)</text></g><path d="M139.5 55h10"></path><path d="M149.5 55h10"></path><g class="terminal ">
<path d="M159.5 55h0.0"></path><path d="M205.0 55h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="159.5" y="44"></rect><text x="182.25" y="59">':'</text></g><path d="M205.0 55h10"></path><path d="M215.0 55h10"></path><g class="terminal ">
<path d="M225.0 55h0.0"></path><path d="M304.5 55h0.0"></path><rect height="22" rx="10" ry="10" width="79.5" x="225.0" y="44"></rect><text x="264.75" y="59">W:(0-9)</text></g><path d="M304.5 55h10"></path><path d="M314.5 55h10"></path><g class="terminal ">
<path d="M324.5 55h0.0"></path><path d="M370.0 55h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="324.5" y="44"></rect><text x="347.25" y="59">':'</text></g><path d="M370.0 55h10"></path><path d="M380.0 55h10"></path><g class="terminal ">
<path d="M390.0 55h0.0"></path><path d="M469.5 55h0.0"></path><rect height="22" rx="10" ry="10" width="79.5" x="390.0" y="44"></rect><text x="429.75" y="59">W:(0-9)</text></g></g><g class="non-terminal ">
<path d="M50.0 28h0.0"></path><path d="M123.0 28h0.0"></path><text class="comment" x="86.5" y="33">&#91;combine&#93;</text></g></g><path d="M479.5 55h10"></path><path d="M 489.5 55 h 20 m -10 -10 v 20 m 10 -20 v 20"></path></g></svg>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,361 @@
# jsonParser.py
#
# Copyright (c) 2006, Paul McGuire
#
test1 = """
{
"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" : []
}
]
}
}
}
"""
test2 = """
{"menu": {
"id": "file",
"value": "File:",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
"""
test3 = """
{"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;"
}
}}
"""
test4 = """
{"web-app": {
"servlet": [ // Defines the CDSServlet
{
"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:".
*/
"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.
*/
"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.
*/
"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 acquiring 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.
*/
"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.
*/
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
/*
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
*/
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
/*
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.
*/
"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.
*/
"redirectionClass": "org.cofax.SqlRedirection",
/*
Defines the data store name. Keep this at the default
*/
"dataStoreName": "cofax",
/*
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
*/
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
/*
Defines the user name to connect to the database
*/
"dataStoreUser": "sa",
/*
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.
*/
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
/*
A log file to print out database information
*/
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
/*
The number of connection to initialize on startup
*/
"dataStoreInitConns": 10,
/*
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
*/
"dataStoreConnUsageLimit": 100,
/*
The level of information to print to the log
*/
"dataStoreLogLevel": "debug",
/*
The maximum URL length allowable by the CDS Servlet
Helps to prevent hacking
*/
"maxUrlLength": 500}},
/*
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
*/
"mailHost": "mail1",
/*
An override
*/
"mailHostOverride": "mail2"}},
/*
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
*/
{
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"},
{
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
/*
Path to the template folder relative to the tools tomcat installation.
*/
"templatePath": "toolstemplates/",
/*
Logging boolean 1 = on, 0 = off
*/
"log": 1,
/*
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.
*/
"logMaxSize": "",
/*
DataStore logging boolean 1 = on, 0 = off
*/
"dataLog": 1,
/*
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.
*/
"dataLogMaxSize": "",
/*
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.
*/
"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.
*/
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
/*
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.
*/
"adminGroupID": 4,
/*
Is the tools app running on the 'beta server'.
*/
"betaServer": true}}],
"servlet-mapping": {
/*
URL mapping for the CDS Servlet
*/
"cofaxCDS": "/",
/*
URL mapping for the Email Servlet
*/
"cofaxEmail": "/cofaxutil/aemail/*",
/*
URL mapping for the Admin servlet
*/
"cofaxAdmin": "/admin/*",
/*
URL mapping for the Files servlet
*/
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"},
/*
New! The cofax taglib descriptor file
*/
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
"""
test5 = """
{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"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"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}
"""

View File

@@ -0,0 +1,14 @@
[users]
source_dir = '/home/karthik/Projects/python'
data_dir = '/home/karthik/Projects/data'
result_dir = '/home/karthik/Projects/Results'
param_file = $result_dir/param_file
res_file = $result_dir/result_file
comment = 'this is a comment'
; a line starting with ';' is a comment
K = 8
simulate_K = 0
N = 4000
mod_scheme = 'QPSK'
Na = K+2

View File

@@ -0,0 +1,14 @@
import pyparsing as pp
# first, some basic validation: forward is a ParserElement, so is Literal
# MatchFirst([Forward(), Literal(...)]) should also be okay
e: pp.ParserElement = pp.Forward()
e = pp.Literal()
e = pp.MatchFirst([pp.Forward(), pp.Literal("hi there")])
# confirm that it isn't returning Any because it cannot be assigned to a str
x: str = pp.Forward() | pp.Literal("oops") # type: ignore[assignment]
# confirm that `Forward.__or__` has the right behavior
e = pp.Forward() | pp.Literal("nice to meet you")
# and that it isn't returning Any because it cannot be assigned to an int
y: int = pp.Forward() | pp.Literal("oops") # type: ignore[assignment]

View File

@@ -0,0 +1,318 @@
import unittest
from io import StringIO
from pathlib import Path
from typing import List
from examples.jsonParser import jsonObject
from examples.simpleBool import boolExpr
from examples.simpleSQL import simpleSQL
from examples.mozillaCalendarParser import calendars
from pyparsing.diagram import to_railroad, railroad_to_html, NamedDiagram, AnnotatedItem
import pyparsing as pp
import railroad
import tempfile
import os
import sys
print(f"Running {__file__}")
print(sys.version_info)
curdir = Path(__file__).parent
def is_run_with_coverage():
"""Check whether test is run with coverage.
From https://stackoverflow.com/a/69812849/165216
"""
gettrace = getattr(sys, "gettrace", None)
if gettrace is None:
return False
else:
gettrace_result = gettrace()
try:
from coverage.pytracer import PyTracer
from coverage.tracer import CTracer
if isinstance(gettrace_result, (CTracer, PyTracer)):
return True
except ImportError:
pass
return False
def running_in_debug() -> bool:
"""
Returns True if we're in debug mode (determined by either setting
environment var, or running in a debugger which sets sys.settrace)
"""
return (
os.environ.get("RAILROAD_DEBUG", False)
or sys.gettrace()
and not is_run_with_coverage()
)
class TestRailroadDiagrams(unittest.TestCase):
def get_temp(self):
"""
Returns an appropriate temporary file for writing a railroad diagram
"""
delete_on_close = not running_in_debug()
return tempfile.NamedTemporaryFile(
dir=".",
delete=delete_on_close,
mode="w",
encoding="utf-8",
suffix=".html",
)
def generate_railroad(
self, expr: pp.ParserElement, label: str, show_results_names: bool = False
) -> List[NamedDiagram]:
"""
Generate an intermediate list of NamedDiagrams from a pyparsing expression.
"""
with self.get_temp() as temp:
railroad = to_railroad(expr, show_results_names=show_results_names)
temp.write(railroad_to_html(railroad))
if running_in_debug():
print(f"{label}: {temp.name}")
return railroad
def test_example_rr_diags(self):
subtests = [
("jsonObject", jsonObject, 8),
("boolExpr", boolExpr, 6),
("simpleSQL", simpleSQL, 20),
("calendars", calendars, 13),
]
for label, example_expr, expected_rr_len in subtests:
with self.subTest(f"{label}: test rr diag without results names"):
railroad = self.generate_railroad(example_expr, example_expr)
if len(railroad) != expected_rr_len:
diag_html = railroad_to_html(railroad)
for line in diag_html.splitlines():
if 'h1 class="railroad-heading"' in line:
print(line)
assert (
len(railroad) == expected_rr_len
), f"expected {expected_rr_len}, got {len(railroad)}"
with self.subTest(f"{label}: test rr diag with results names"):
railroad = self.generate_railroad(
example_expr, example_expr, show_results_names=True
)
if len(railroad) != expected_rr_len:
print(railroad_to_html(railroad))
assert (
len(railroad) == expected_rr_len
), f"expected {expected_rr_len}, got {len(railroad)}"
def test_nested_forward_with_inner_and_outer_names(self):
outer = pp.Forward().setName("outer")
inner = pp.Word(pp.alphas)[...].setName("inner")
outer <<= inner
railroad = self.generate_railroad(outer, "inner_outer_names")
assert len(railroad) == 2
railroad = self.generate_railroad(
outer, "inner_outer_names", show_results_names=True
)
assert len(railroad) == 2
def test_nested_forward_with_inner_name_only(self):
outer = pp.Forward()
inner = pp.Word(pp.alphas)[...].setName("inner")
outer <<= inner
railroad = self.generate_railroad(outer, "inner_only")
assert len(railroad) == 1
railroad = self.generate_railroad(outer, "inner_only", show_results_names=True)
assert len(railroad) == 1
def test_each_grammar(self):
grammar = pp.Each(
[
pp.Word(pp.nums),
pp.Word(pp.alphas),
pp.pyparsing_common.uuid,
]
).setName("int-word-uuid in any order")
railroad = self.generate_railroad(grammar, "each_expression")
assert len(railroad) == 2
railroad = self.generate_railroad(
grammar, "each_expression", show_results_names=True
)
assert len(railroad) == 2
def test_none_name(self):
grammar = pp.Or(["foo", "bar"])
railroad = to_railroad(grammar)
assert len(railroad) == 1
assert railroad[0].name is not None
def test_none_name2(self):
grammar = pp.Or(["foo", "bar"]) + pp.Word(pp.nums).setName("integer")
railroad = to_railroad(grammar)
assert len(railroad) == 2
assert railroad[0].name is not None
railroad = to_railroad(grammar, show_results_names=True)
assert len(railroad) == 2
def test_complete_combine_element(self):
ints = pp.Word(pp.nums)
grammar = pp.Combine(
ints("hours") + ":" + ints("minutes") + ":" + ints("seconds")
)
railroad = to_railroad(grammar)
assert len(railroad) == 1
railroad = to_railroad(grammar, show_results_names=True)
assert len(railroad) == 1
def test_create_diagram(self):
ints = pp.Word(pp.nums)
grammar = pp.Combine(
ints("hours") + ":" + ints("minutes") + ":" + ints("seconds")
)
diag_strio = StringIO()
grammar.create_diagram(output_html=diag_strio)
diag_str = diag_strio.getvalue().lower()
tags = "<html> </html> <head> </head> <body> </body>".split()
assert all(tag in diag_str for tag in tags)
def test_create_diagram_embed(self):
ints = pp.Word(pp.nums)
grammar = pp.Combine(
ints("hours") + ":" + ints("minutes") + ":" + ints("seconds")
)
diag_strio = StringIO()
grammar.create_diagram(output_html=diag_strio, embed=True)
diag_str = diag_strio.getvalue().lower()
tags = "<html> </html> <head> </head> <body> </body>".split()
assert not any(tag in diag_str for tag in tags)
def test_create_diagram_for_oneormore_with_stopon(self):
wd = pp.Word(pp.alphas)
grammar = "start" + wd[1, ...:"end"] + "end"
pp.autoname_elements()
railroad_diag = to_railroad(grammar)
assert len(railroad_diag) == 3
assert isinstance(railroad_diag[1].diagram.items[1].item, railroad.Sequence)
assert isinstance(
railroad_diag[1].diagram.items[1].item.items[0], AnnotatedItem
)
assert isinstance(
railroad_diag[1].diagram.items[1].item.items[1], railroad.NonTerminal
)
def test_kwargs_pass_thru_create_diagram(self):
from io import StringIO
# Creates a simple diagram with a blue body and
# various other railroad features colored with
# a complete disregard for taste
# Very simple grammar for demo purposes
salutation = pp.Word(pp.alphas).set_name("salutation")
subject = pp.rest_of_line.set_name("subject")
parse_grammar = salutation + subject
# This is used to turn off the railroads
# definition of DEFAULT_STYLE.
# If this is set to 'None' the default style
# will be written as part of each diagram
# and will you will not be able to set the
# css style globally and the string 'expStyle'
# will have no effect.
# There is probably a PR to railroad_diagram to
# remove some cruft left in the SVG.
DEFAULT_STYLE = ""
# CSS Code to be placed into head of the html file
expStyle = """
<style type="text/css">
body {
background-color: blue;
}
.railroad-heading {
font-family: monospace;
color: bisque;
}
svg.railroad-diagram {
background-color: hsl(264,45%,85%);
}
svg.railroad-diagram path {
stroke-width: 3;
stroke: green;
fill: rgba(0,0,0,0);
}
svg.railroad-diagram text {
font: bold 14px monospace;
text-anchor: middle;
white-space: pre;
}
svg.railroad-diagram text.diagram-text {
font-size: 12px;
}
svg.railroad-diagram text.diagram-arrow {
font-size: 16px;
}
svg.railroad-diagram text.label {
text-anchor: start;
}
svg.railroad-diagram text.comment {
font: italic 12px monospace;
}
svg.railroad-diagram g.non-terminal text {
/*font-style: italic;*/
}
svg.railroad-diagram rect {
stroke-width: 3;
stroke: black;
fill: hsl(55, 72%, 69%);
}
svg.railroad-diagram rect.group-box {
stroke: rgb(33, 8, 225);
stroke-dasharray: 10 5;
fill: none;
}
svg.railroad-diagram path.diagram-text {
stroke-width: 3;
stroke: black;
fill: white;
cursor: help;
}
svg.railroad-diagram g.diagram-text:hover path.diagram-text {
fill: #eee;
}
</style>
"""
# the 'css=DEFAULT_STYLE' or 'css=""' is needed to turn off railroad_diagrams styling
diag_html_capture = StringIO()
parse_grammar.create_diagram(
diag_html_capture,
vertical=6,
show_results_names=True,
css=DEFAULT_STYLE,
head=expStyle,
)
self.assertIn(expStyle, diag_html_capture.getvalue())
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,71 @@
#
# test_examples.py
#
from importlib import import_module
import unittest
from pyparsing import testing as ppt
class TestExamples(unittest.TestCase):
def _run(self, name):
mod = import_module("examples." + name)
# use pyparsing context to reset each test to clean
# pyparsing settings
with ppt.reset_pyparsing_context():
getattr(mod, "main", lambda *args, **kwargs: None)()
def test_numerics(self):
self._run("numerics")
def test_parse_python_value(self):
self._run("parse_python_value")
def test_tap(self):
self._run("TAP")
def test_roman_numerals(self):
self._run("roman_numerals")
def test_sexp_parser(self):
self._run("sexpParser")
def test_oc(self):
self._run("oc")
def test_delta_time(self):
self._run("delta_time")
def test_eval_arith(self):
self._run("eval_arith")
def test_select_parser(self):
self._run("select_parser")
def test_booleansearchparser(self):
self._run("booleansearchparser")
def test_rosettacode(self):
self._run("rosettacode")
def test_excelExpr(self):
self._run("excel_expr")
def test_lucene_grammar(self):
self._run("lucene_grammar")
def test_range_check(self):
self._run("range_check")
def test_stackish(self):
self._run("stackish")
def test_email_parser(self):
self._run("email_address_parser")
def test_mongodb_query_parser(self):
self._run("mongodb_query_expression")
def test_lox_parser(self):
self._run("lox_parser")

View File

@@ -0,0 +1,88 @@
#
# tests copied from matplotlib.tests.test_mathtext
#
import platform
import re
import pytest
try:
import matplotlib.mathtext as mpl_mathtext
except ImportError:
mpl_mathtext = None
# fmt: off
@pytest.mark.parametrize(
"math, msg",
[
(r"$\hspace{}$", r"Expected \hspace{space}"),
(r"$\hspace{foo}$", r"Expected \hspace{space}"),
(r"$\sinx$", r"Unknown symbol: \sinx"),
(r"$\dotx$", r"Unknown symbol: \dotx"),
(r"$\frac$", r"Expected \frac{num}{den}"),
(r"$\frac{}{}$", r"Expected \frac{num}{den}"),
(r"$\binom$", r"Expected \binom{num}{den}"),
(r"$\binom{}{}$", r"Expected \binom{num}{den}"),
(r"$\genfrac$", r"Expected \genfrac{ldelim}{rdelim}{rulesize}{style}{num}{den}"),
(r"$\genfrac{}{}{}{}{}{}$", r"Expected \genfrac{ldelim}{rdelim}{rulesize}{style}{num}{den}"),
(r"$\sqrt$", r"Expected \sqrt{value}"),
(r"$\sqrt f$", r"Expected \sqrt{value}"),
(r"$\overline$", r"Expected \overline{body}"),
(r"$\overline{}$", r"Expected \overline{body}"),
(r"$\leftF$", r"Expected a delimiter"),
(r"$\rightF$", r"Unknown symbol: \rightF"),
(r"$\left(\right$", r"Expected a delimiter"),
# PyParsing 2 uses double quotes, PyParsing 3 uses single quotes and an
# extra backslash.
(r"$\left($", re.compile(r'Expected ("|\'\\)\\right["\']')),
(r"$\dfrac$", r"Expected \dfrac{num}{den}"),
(r"$\dfrac{}{}$", r"Expected \dfrac{num}{den}"),
(r"$\overset$", r"Expected \overset{annotation}{body}"),
(r"$\underset$", r"Expected \underset{annotation}{body}"),
(r"$\foo$", r"Unknown symbol: \foo"),
(r"$a^2^2$", r"Double superscript"),
(r"$a_2_2$", r"Double subscript"),
(r"$a^2_a^2$", r"Double superscript"),
],
ids=[
"hspace without value",
"hspace with invalid value",
"function without space",
"accent without space",
"frac without parameters",
"frac with empty parameters",
"binom without parameters",
"binom with empty parameters",
"genfrac without parameters",
"genfrac with empty parameters",
"sqrt without parameters",
"sqrt with invalid value",
"overline without parameters",
"overline with empty parameter",
"left with invalid delimiter",
"right with invalid delimiter",
"unclosed parentheses with sizing",
"unclosed parentheses without sizing",
"dfrac without parameters",
"dfrac with empty parameters",
"overset without parameters",
"underset without parameters",
"unknown symbol",
"double superscript",
"double subscript",
"super on sub without braces",
],
)
@pytest.mark.skipif("mpl_mathtext is None")
def test_mathtext_exceptions(math, msg):
parser = mpl_mathtext.MathTextParser("agg")
match = re.escape(msg) if isinstance(msg, str) else msg
with pytest.raises(ValueError, match=match):
parser.parse(math)
# fmt: on
@pytest.mark.skipif("mpl_mathtext is None")
def test_get_unicode_index_exception():
with pytest.raises(ValueError):
mpl_mathtext.get_unicode_index(r"\foo")

View File

@@ -0,0 +1,631 @@
from pyparsing.tools.cvt_pyparsing_pep8_names import (
pep8_converter, pre_pep8_arg_names, pre_pep8_method_names, special_changes
)
import pytest
def test_conversion_composed():
orig = ("\n".join(
f"{method_name}()"
for method_name in sorted(pre_pep8_method_names) + list(special_changes)
) + "\n"
+ "\n".join(
f"fn(100, {arg_name}=True)"
for arg_name in sorted(pre_pep8_arg_names)
))
expected = """\
add_condition()
add_parse_action()
any_close_tag()
any_open_tag()
as_dict()
as_list()
c_style_comment()
can_parse_next()
condition_as_parse_action()
convert_to_date()
convert_to_datetime()
convert_to_float()
convert_to_integer()
counted_array()
cpp_style_comment()
dbl_quoted_string()
dbl_slash_comment()
default_name()
dict_of()
disable_memoization()
downcase_tokens()
enable_left_recursion()
enable_packrat()
get_name()
html_comment()
ignore_whitespace()
indented_block()
infix_notation()
inline_literals_using()
java_style_comment()
leave_whitespace()
line_end()
line_start()
located_expr()
match_only_at_col()
match_previous_expr()
match_previous_literal()
nested_expr()
null_debug_action()
one_of()
original_text_for()
parse_file()
parse_string()
parse_with_tabs()
python_style_comment()
quoted_string()
remove_quotes()
replace_with()
reset_cache()
rest_of_line()
run_tests()
scan_string()
search_string()
set_break()
set_debug()
set_debug_actions()
set_default_whitespace_chars()
set_fail_action()
set_name()
set_parse_action()
set_results_name()
set_whitespace_chars()
sgl_quoted_string()
string_end()
string_start()
token_map()
trace_parse_action()
transform_string()
try_parse()
unicode_string()
upcase_tokens()
with_attribute()
with_class()
OpAssoc()
DelimitedList()
DelimitedList()
replace_html_entity()
make_html_tags()
make_xml_tags()
common_html_entity()
strip_html_tags()
fn(100, as_group_list=True)
fn(100, as_keyword=True)
fn(100, as_match=True)
fn(100, as_string=True)
fn(100, body_chars=True)
fn(100, call_during_try=True)
fn(100, convert_whitespace_escapes=True)
fn(100, end_quote_char=True)
fn(100, esc_char=True)
fn(100, esc_quote=True)
fn(100, exclude_chars=True)
fn(100, fail_on=True)
fn(100, failure_tests=True)
fn(100, full_dump=True)
fn(100, ident_chars=True)
fn(100, ignore_expr=True)
fn(100, include_separators=True)
fn(100, init_chars=True)
fn(100, int_expr=True)
fn(100, join_string=True)
fn(100, list_all_matches=True)
fn(100, marker_string=True)
fn(100, match_string=True)
fn(100, max_matches=True)
fn(100, max_mismatches=True)
fn(100, not_chars=True)
fn(100, parse_all=True)
fn(100, post_parse=True)
fn(100, print_results=True)
fn(100, quote_char=True)
fn(100, stop_on=True)
fn(100, unquote_results=True)
fn(100, use_regex=True)
fn(100, word_chars=True)"""
converted = pep8_converter.transform_string(orig)
assert converted == expected
def test_conversion_examples():
orig = r"""
L = equation.parseString(input_string)
equation.runTests((t[1] for t in testcases), postParse=post_test)
L = pattern.parseString(input_string, parseAll=True)
itemRef = pp.OneOrMore(pp.Word(pp.alphas)).set_parse_action(self.validate_item_name).setName("item_ref")
doorsCommand = doorsVerb.setName("DOORS")
originalTextFor,
cStyleComment,
oneOf,
delimitedList,
oneOf(list(r"nrtbf\">" + "'")) | ("u" + Word(hexnums, exact=4)) | SGL_PRINTABLE
| (SCOPE_.suppress() + delimitedList(id) + SEMI)
| (SCOPE_.suppress() + ACTION + SCOPE_.suppress() + delimitedList(id) + SEMI)
(id("result_name") + oneOf("= +=")("labelOp") + atom("atom") + Optional(ebnfSuffix))
| (id("result_name") + oneOf("= +=")("labelOp") + block + Optional(ebnfSuffix))
antlrGrammarTree = grammar().parseString(text)
pyparsingTree = pyparsingRule.parseString("2 - 5 * 42 + 7 / 25")
antlr_grammar.optionsSpec.parseString(text) # @UndefinedVariable
antlr_grammar.tokensSpec.parseString(text) # @UndefinedVariable
antlr_grammar.block.parseString(text) # @UndefinedVariable
antlr_grammar.rule.parseString(text) # @UndefinedVariable
# antlr_grammar.rule.parseString(text) #@UndefinedVariable
antlrGrammarTree = antlr_grammar.grammarDef.parseString(
pyparsingTree = pyparsingRule.parseString("2 - 5 * 42 + 7 / 25")
pyparsingTreeList = pyparsingTree.asList()
api_scanner = apiRef.scanString(test)
api_scanner = apiRef.scanString(test)
BigQueryViewParser._get_parser().parseString(sql_stmt, parseAll=True)
ParserElement.enablePackrat()
ungrouped_select_stmt = Forward().setName("select statement")
QUOTED_BRACKETS = QuotedString("[", endQuoteChar="]")
expr = Forward().setName("expression")
bind_parameter = Word("?", nums) | Combine(oneOf(": @ $") + parameter_name)
type_name = oneOf(
date_part = oneOf(
+ delimitedList(function_arg)
partition_expression_list = delimitedList(grouping_term)(
+ Optional(ORDER + BY + delimitedList(ordering_term))
Optional(ARRAY + Optional(LT + delimitedList(type_name) + GT))
+ delimitedList(expr)
+ Optional(LT + delimitedList(type_name) + GT)
+ Optional(delimitedList(expr + Optional(AS + identifier)))
struct_term = LPAR + delimitedList(expr_term) + RPAR
expr <<= infixNotation(
(oneOf("- + ~") | NOT, UNARY, opAssoc.RIGHT),
(ISNULL | NOTNULL | NOT + NULL, UNARY, opAssoc.LEFT),
("||", BINARY, opAssoc.LEFT),
(oneOf("* / %"), BINARY, opAssoc.LEFT),
(oneOf("+ -"), BINARY, opAssoc.LEFT),
(oneOf("<< >> & |"), BINARY, opAssoc.LEFT),
(oneOf("= > < >= <= <> != !< !> =="), BINARY, opAssoc.LEFT),
opAssoc.LEFT,
((BETWEEN, AND), TERNARY, opAssoc.LEFT),
+ Group(ungrouped_select_stmt | delimitedList(expr))
(AND, BINARY, opAssoc.LEFT),
(OR, BINARY, opAssoc.LEFT),
| USING + LPAR + Group(delimitedList(qualified_column_name)) + RPAR
identifier_list = t.asList()
).setParseAction(record_table_identifier)
).setParseAction(record_quoted_table_identifier)
).setName("table_identifier")
over_partition = (PARTITION + BY + delimitedList(partition_expression_list))(
over_order = ORDER + BY + delimitedList(ordering_term)
EXCEPT + LPAR + delimitedList(column_name) + RPAR
with_stmt = Forward().setName("with statement")
delimitedList(
GROUP + BY + Group(delimitedList(grouping_term))("group_by_terms")
ORDER + BY + Group(delimitedList(ordering_term))("order_by_terms")
+ Optional(delimitedList(window_select_clause))
sql_comment = oneOf("-- #") + restOfLine | cStyleComment
identifier.setParseAction(record_with_alias)
with_stmt <<= WITH + delimitedList(with_clause)
return bibfile.parseString(str)
stmt.runTests('''\
(Literal("#").suppress() + Word(nums)).setParseAction(
+ Optional(Group(delimitedList(identifier | number_value | string_value)))
i.setParseAction(action)
retval[f] = object_definition.parseFile(f)
vert + pp.Word(pp.alphas) + vert + pp.delimitedList(number, "|") + vert
results = BNF().parseString(s, parseAll=True)
cStyleComment.suppress() | fn_typedef.suppress() | func_def
if fn.fn_args.asList() != [["void"]]:
fullDump=False,
dblQuotedString,
removeQuotes,
ipAddress = delimitedList(integer, ".", combine=True)
ipAddress.setResultsName("ipAddr")
+ ("-" | Word(alphas + nums + "@._")).setResultsName("auth")
+ serverDateTime.setResultsName("timestamp")
+ dblQuotedString.setResultsName("cmd").setParseAction(getCmdFields)
+ (integer | "-").setResultsName("statusCode")
+ (integer | "-").setResultsName("numBytesSent")
+ dblQuotedString.setResultsName("referrer").setParseAction(removeQuotes)
+ dblQuotedString.setResultsName("clientSfw").setParseAction(removeQuotes)
fields = getLogLineBNF().parseString(line)
+ include_directive.transformString(included_file_contents)
# use include_directive.transformString to perform includes
expanded_source = include_directive.transformString(initial_file)
"def" + identifier + Group("(" + Optional(delimitedList(identifier)) + ")") + ":"
invre = GroupEmitter(parser().parseString(regex)).make_generator()
(pp.one_of("! -"), 1, pp.opAssoc.RIGHT),
(pp.one_of("/ *"), 2, pp.opAssoc.LEFT),
(pp.one_of("- +"), 2, pp.opAssoc.LEFT),
(pp.one_of("> >= < <="), 2, pp.opAssoc.LEFT),
(pp.one_of("!= =="), 2, pp.opAssoc.LEFT),
(AND, 2, pp.opAssoc.LEFT),
(OR, 2, pp.opAssoc.LEFT),
("^", 2, pp.opAssoc.LEFT),
((NOT | pp.oneOf("# - ~")).set_name("not op"), 1, pp.opAssoc.RIGHT),
(pp.oneOf("* / // %"), 2, pp.opAssoc.LEFT),
(pp.oneOf("+ -"), 2, pp.opAssoc.LEFT),
("..", 2, pp.opAssoc.LEFT),
(pp.oneOf("<< >>"), 2, pp.opAssoc.LEFT),
("&", 2, pp.opAssoc.LEFT),
("~", 2, pp.opAssoc.LEFT),
("|", 2, pp.opAssoc.LEFT),
(pp.oneOf("< > <= >= ~= =="), 2, pp.opAssoc.LEFT),
result = lua_script.parseString(sample)
success2, _ = expression.run_tests(failtests, failureTests=True)
UID DTSTAMP LAST-MODIFIED X RRULE EXDATE''', asKeyword=True
return opening + wiki_markup.transformString(t[1][1:-1]) + closing
t["link_text"] = wiki_markup.transformString(link_text)
postParse=lambda _, s: "{:,}".format(s[0]),
ast = program.parse_string(test, parseAll=True)
expr.copy().addCondition(
print(row.parseString(line).dump())
+ pp.Word("ACGTN")[1, ...].addParseAction("".join)("gene")
for t, startLoc, endLoc in searchseq.scanString(g.gene, overlap=True):
return Empty().setParseAction(lambda s, l, t: t.__setitem__(name, l))
quotedString,
OPTION_ - ident("optionName") + EQ + quotedString("optionValue") + SEMI
return cls(t[0].asList())
(bnfToken | quotedString | optionalTerm | (LPAREN + bnfExpr + RPAREN))
).setName("street_address")
).setName("header with various elements")("header")
(plus_minus().setName("pos_neg"), 1, pp.opAssoc.RIGHT),
(mult_div, 2, pp.opAssoc.LEFT),
(plus_minus, 2, pp.opAssoc.LEFT),
]).setName("simple_arithmetic")
).setName("grammar")
restOfLine,
replaceWith,
+ ident.setResultsName("name")
+ restOfLine.setResultsName("value")
operatorWord = Group(Combine(Word(alphanums) + Suppress("*"))).setResultsName(
) | Group(Word(alphanums)).setResultsName("word")
Group(Suppress('"') + operatorQuotesContent + Suppress('"')).setResultsName(
Group(Suppress("(") + operatorOr + Suppress(")")).setResultsName(
Group(Suppress(Keyword("not", caseless=True)) + operatorNot).setResultsName(
).setResultsName("and")
operatorNot + OneOrMore(~oneOf("and or") + operatorAnd)
).setResultsName("or")
return operatorOr.parseString
return self._methods[argument.getName()](argument)
sexp.run_tests(alltests, fullDump=False)
self.__dict__.update(tokens.asDict())
shape = shapeExpr.parseString(t)[0]
(factop, 1, opAssoc.LEFT),
(expop, 2, opAssoc.RIGHT),
(signop, 1, opAssoc.RIGHT),
(multop, 2, opAssoc.LEFT),
(plusop, 2, opAssoc.LEFT),
print(expr.parseString(t))
+ Word(alphas, alphanums + "_").setResultsName("tablename")
+ field_list_def.setResultsName("columns")
+ Word(alphanums + "_").setResultsName("fromtable")
+ Word(alphanums + "_").setResultsName("fromcolumn")
+ Word(alphanums + "_").setResultsName("totable")
+ Word(alphanums + "_").setResultsName("tocolumn")
self.assertEqual("2t", name_type.parseString("2t")[0])
self.assertRaises(ParseException, name_type.parseString, "2t")
self.assertRaises(ParseException, name_type.parseString, char)
self.assertEqual("simple_test", name_type.parseString("simple_test")[0])
self.assertRaises(ParseException, mr.parseString, "2t")
self.assertRaises(ParseException, mr.parseString, char)
self.assertEqual("simple_test", mr.parseString("simple_test")[0].name)
self.assertEqual("1066", bp.number.parseString("1066")[0])
self.assertEqual("0", bp.number.parseString("0")[0])
self.assertRaises(ParseException, bp.number.parseString, "-4")
self.assertRaises(ParseException, bp.number.parseString, "+4")
self.assertRaises(ParseException, bp.number.parseString, ".4")
self.assertEqual("0", bp.number.parseString("0.4")[0])
self.assertEqual(bp.chars_no_quotecurly.parseString("x")[0], "x")
self.assertEqual(bp.chars_no_quotecurly.parseString("a string")[0], "a string")
self.assertEqual(bp.chars_no_quotecurly.parseString('a "string')[0], "a ")
self.assertEqual(bp.chars_no_curly.parseString("x")[0], "x")
self.assertEqual(bp.chars_no_curly.parseString("a string")[0], "a string")
self.assertEqual(bp.chars_no_curly.parseString("a {string")[0], "a ")
self.assertEqual(bp.chars_no_curly.parseString("a }string")[0], "a ")
self.assertEqual(obj.parseString("{}").asList(), [])
self.assertEqual(obj.parseString('{a "string}')[0], 'a "string')
obj.parseString("{a {nested} string}").asList(),
obj.parseString("{a {double {nested}} string}").asList(),
self.assertEqual([], obj.parseString('""').asList())
self.assertEqual("a string", obj.parseString('"a string"')[0])
obj.parseString('"a {nested} string"').asList(),
obj.parseString('"a {double {nested}} string"').asList(),
self.assertEqual(Macro("someascii"), bp.string.parseString("someascii")[0])
self.assertRaises(ParseException, bp.string.parseString, "%#= validstring")
self.assertEqual(bp.string.parseString("1994")[0], "1994")
self.assertEqual(Macro("aname"), fv.parseString("aname")[0])
self.assertEqual(Macro("aname"), fv.parseString("ANAME")[0])
fv.parseString('aname # "some string"').asList(),
fv.parseString("aname # {some {string}}").asList(),
["a string", "1994"], fv.parseString('"a string" # 1994').asList()
fv.parseString('"a string" # 1994 # a_macro').asList(),
res = bp.comment.parseString("@Comment{about something}")
self.assertEqual(res.asList(), ["comment", "{about something}"])
bp.comment.parseString("@COMMENT{about something").asList(),
bp.comment.parseString("@comment(about something").asList(),
bp.comment.parseString("@COMment about something").asList(),
ParseException, bp.comment.parseString, "@commentabout something"
ParseException, bp.comment.parseString, "@comment+about something"
ParseException, bp.comment.parseString, '@comment"about something'
res = bp.preamble.parseString('@preamble{"about something"}')
self.assertEqual(res.asList(), ["preamble", "about something"])
bp.preamble.parseString("@PREamble{{about something}}").asList(),
bp.preamble.parseString(
).asList(),
res = bp.macro.parseString('@string{ANAME = "about something"}')
self.assertEqual(res.asList(), ["string", "aname", "about something"])
bp.macro.parseString("@string{aname = {about something}}").asList(),
res = bp.entry.parseString(txt)
res.asList(),
res = bp.bibfile.parseString(txt)
self.assertEqual(res.asList(), res2.asList())
res3 = [r.asList()[0] for r, start, end in bp.definitions.scanString(txt)]
self.assertEqual(res.asList(), res3)
print(toks.asList())
parsed = ppc.url.parseString(url)
cppStyleComment,
+ restOfLine
Regex(r"\\\S+").setParseAction(lambda t: t[0][1:]).set_name("escapedIdent")
) # .setDebug()
joinString=" ",
| (LPAR + Group(expr) + RPAR).set_name("nestedExpr")
delay = Group("#" + delayArg).set_name("delay") # .setDebug()
stmt = Forward().set_name("stmt") # .setDebug()
verilogbnf.ignore(cppStyleComment)
return ret.set_parse_action(pp.replaceWith(val))
"""
expected = r"""
L = equation.parse_string(input_string)
equation.run_tests((t[1] for t in testcases), post_parse=post_test)
L = pattern.parse_string(input_string, parse_all=True)
itemRef = pp.OneOrMore(pp.Word(pp.alphas)).set_parse_action(self.validate_item_name).set_name("item_ref")
doorsCommand = doorsVerb.set_name("DOORS")
original_text_for,
c_style_comment,
one_of,
DelimitedList,
one_of(list(r"nrtbf\">" + "'")) | ("u" + Word(hexnums, exact=4)) | SGL_PRINTABLE
| (SCOPE_.suppress() + DelimitedList(id) + SEMI)
| (SCOPE_.suppress() + ACTION + SCOPE_.suppress() + DelimitedList(id) + SEMI)
(id("result_name") + one_of("= +=")("labelOp") + atom("atom") + Optional(ebnfSuffix))
| (id("result_name") + one_of("= +=")("labelOp") + block + Optional(ebnfSuffix))
antlrGrammarTree = grammar().parse_string(text)
pyparsingTree = pyparsingRule.parse_string("2 - 5 * 42 + 7 / 25")
antlr_grammar.optionsSpec.parse_string(text) # @UndefinedVariable
antlr_grammar.tokensSpec.parse_string(text) # @UndefinedVariable
antlr_grammar.block.parse_string(text) # @UndefinedVariable
antlr_grammar.rule.parse_string(text) # @UndefinedVariable
# antlr_grammar.rule.parse_string(text) #@UndefinedVariable
antlrGrammarTree = antlr_grammar.grammarDef.parse_string(
pyparsingTree = pyparsingRule.parse_string("2 - 5 * 42 + 7 / 25")
pyparsingTreeList = pyparsingTree.as_list()
api_scanner = apiRef.scan_string(test)
api_scanner = apiRef.scan_string(test)
BigQueryViewParser._get_parser().parse_string(sql_stmt, parse_all=True)
ParserElement.enable_packrat()
ungrouped_select_stmt = Forward().set_name("select statement")
QUOTED_BRACKETS = QuotedString("[", end_quote_char="]")
expr = Forward().set_name("expression")
bind_parameter = Word("?", nums) | Combine(one_of(": @ $") + parameter_name)
type_name = one_of(
date_part = one_of(
+ DelimitedList(function_arg)
partition_expression_list = DelimitedList(grouping_term)(
+ Optional(ORDER + BY + DelimitedList(ordering_term))
Optional(ARRAY + Optional(LT + DelimitedList(type_name) + GT))
+ DelimitedList(expr)
+ Optional(LT + DelimitedList(type_name) + GT)
+ Optional(DelimitedList(expr + Optional(AS + identifier)))
struct_term = LPAR + DelimitedList(expr_term) + RPAR
expr <<= infix_notation(
(one_of("- + ~") | NOT, UNARY, OpAssoc.RIGHT),
(ISNULL | NOTNULL | NOT + NULL, UNARY, OpAssoc.LEFT),
("||", BINARY, OpAssoc.LEFT),
(one_of("* / %"), BINARY, OpAssoc.LEFT),
(one_of("+ -"), BINARY, OpAssoc.LEFT),
(one_of("<< >> & |"), BINARY, OpAssoc.LEFT),
(one_of("= > < >= <= <> != !< !> =="), BINARY, OpAssoc.LEFT),
OpAssoc.LEFT,
((BETWEEN, AND), TERNARY, OpAssoc.LEFT),
+ Group(ungrouped_select_stmt | DelimitedList(expr))
(AND, BINARY, OpAssoc.LEFT),
(OR, BINARY, OpAssoc.LEFT),
| USING + LPAR + Group(DelimitedList(qualified_column_name)) + RPAR
identifier_list = t.as_list()
).set_parse_action(record_table_identifier)
).set_parse_action(record_quoted_table_identifier)
).set_name("table_identifier")
over_partition = (PARTITION + BY + DelimitedList(partition_expression_list))(
over_order = ORDER + BY + DelimitedList(ordering_term)
EXCEPT + LPAR + DelimitedList(column_name) + RPAR
with_stmt = Forward().set_name("with statement")
DelimitedList(
GROUP + BY + Group(DelimitedList(grouping_term))("group_by_terms")
ORDER + BY + Group(DelimitedList(ordering_term))("order_by_terms")
+ Optional(DelimitedList(window_select_clause))
sql_comment = one_of("-- #") + rest_of_line | c_style_comment
identifier.set_parse_action(record_with_alias)
with_stmt <<= WITH + DelimitedList(with_clause)
return bibfile.parse_string(str)
stmt.run_tests('''\
(Literal("#").suppress() + Word(nums)).set_parse_action(
+ Optional(Group(DelimitedList(identifier | number_value | string_value)))
i.set_parse_action(action)
retval[f] = object_definition.parse_file(f)
vert + pp.Word(pp.alphas) + vert + pp.DelimitedList(number, "|") + vert
results = BNF().parse_string(s, parse_all=True)
c_style_comment.suppress() | fn_typedef.suppress() | func_def
if fn.fn_args.as_list() != [["void"]]:
full_dump=False,
dbl_quoted_string,
remove_quotes,
ipAddress = DelimitedList(integer, ".", combine=True)
ipAddress.set_results_name("ipAddr")
+ ("-" | Word(alphas + nums + "@._")).set_results_name("auth")
+ serverDateTime.set_results_name("timestamp")
+ dbl_quoted_string.set_results_name("cmd").set_parse_action(getCmdFields)
+ (integer | "-").set_results_name("statusCode")
+ (integer | "-").set_results_name("numBytesSent")
+ dbl_quoted_string.set_results_name("referrer").set_parse_action(remove_quotes)
+ dbl_quoted_string.set_results_name("clientSfw").set_parse_action(remove_quotes)
fields = getLogLineBNF().parse_string(line)
+ include_directive.transform_string(included_file_contents)
# use include_directive.transform_string to perform includes
expanded_source = include_directive.transform_string(initial_file)
"def" + identifier + Group("(" + Optional(DelimitedList(identifier)) + ")") + ":"
invre = GroupEmitter(parser().parse_string(regex)).make_generator()
(pp.one_of("! -"), 1, pp.OpAssoc.RIGHT),
(pp.one_of("/ *"), 2, pp.OpAssoc.LEFT),
(pp.one_of("- +"), 2, pp.OpAssoc.LEFT),
(pp.one_of("> >= < <="), 2, pp.OpAssoc.LEFT),
(pp.one_of("!= =="), 2, pp.OpAssoc.LEFT),
(AND, 2, pp.OpAssoc.LEFT),
(OR, 2, pp.OpAssoc.LEFT),
("^", 2, pp.OpAssoc.LEFT),
((NOT | pp.one_of("# - ~")).set_name("not op"), 1, pp.OpAssoc.RIGHT),
(pp.one_of("* / // %"), 2, pp.OpAssoc.LEFT),
(pp.one_of("+ -"), 2, pp.OpAssoc.LEFT),
("..", 2, pp.OpAssoc.LEFT),
(pp.one_of("<< >>"), 2, pp.OpAssoc.LEFT),
("&", 2, pp.OpAssoc.LEFT),
("~", 2, pp.OpAssoc.LEFT),
("|", 2, pp.OpAssoc.LEFT),
(pp.one_of("< > <= >= ~= =="), 2, pp.OpAssoc.LEFT),
result = lua_script.parse_string(sample)
success2, _ = expression.run_tests(failtests, failure_tests=True)
UID DTSTAMP LAST-MODIFIED X RRULE EXDATE''', as_keyword=True
return opening + wiki_markup.transform_string(t[1][1:-1]) + closing
t["link_text"] = wiki_markup.transform_string(link_text)
post_parse=lambda _, s: "{:,}".format(s[0]),
ast = program.parse_string(test, parse_all=True)
expr.copy().add_condition(
print(row.parse_string(line).dump())
+ pp.Word("ACGTN")[1, ...].add_parse_action("".join)("gene")
for t, startLoc, endLoc in searchseq.scan_string(g.gene, overlap=True):
return Empty().set_parse_action(lambda s, l, t: t.__setitem__(name, l))
quoted_string,
OPTION_ - ident("optionName") + EQ + quoted_string("optionValue") + SEMI
return cls(t[0].as_list())
(bnfToken | quoted_string | optionalTerm | (LPAREN + bnfExpr + RPAREN))
).set_name("street_address")
).set_name("header with various elements")("header")
(plus_minus().set_name("pos_neg"), 1, pp.OpAssoc.RIGHT),
(mult_div, 2, pp.OpAssoc.LEFT),
(plus_minus, 2, pp.OpAssoc.LEFT),
]).set_name("simple_arithmetic")
).set_name("grammar")
rest_of_line,
replace_with,
+ ident.set_results_name("name")
+ rest_of_line.set_results_name("value")
operatorWord = Group(Combine(Word(alphanums) + Suppress("*"))).set_results_name(
) | Group(Word(alphanums)).set_results_name("word")
Group(Suppress('"') + operatorQuotesContent + Suppress('"')).set_results_name(
Group(Suppress("(") + operatorOr + Suppress(")")).set_results_name(
Group(Suppress(Keyword("not", caseless=True)) + operatorNot).set_results_name(
).set_results_name("and")
operatorNot + OneOrMore(~one_of("and or") + operatorAnd)
).set_results_name("or")
return operatorOr.parse_string
return self._methods[argument.get_name()](argument)
sexp.run_tests(alltests, full_dump=False)
self.__dict__.update(tokens.as_dict())
shape = shapeExpr.parse_string(t)[0]
(factop, 1, OpAssoc.LEFT),
(expop, 2, OpAssoc.RIGHT),
(signop, 1, OpAssoc.RIGHT),
(multop, 2, OpAssoc.LEFT),
(plusop, 2, OpAssoc.LEFT),
print(expr.parse_string(t))
+ Word(alphas, alphanums + "_").set_results_name("tablename")
+ field_list_def.set_results_name("columns")
+ Word(alphanums + "_").set_results_name("fromtable")
+ Word(alphanums + "_").set_results_name("fromcolumn")
+ Word(alphanums + "_").set_results_name("totable")
+ Word(alphanums + "_").set_results_name("tocolumn")
self.assertEqual("2t", name_type.parse_string("2t")[0])
self.assertRaises(ParseException, name_type.parse_string, "2t")
self.assertRaises(ParseException, name_type.parse_string, char)
self.assertEqual("simple_test", name_type.parse_string("simple_test")[0])
self.assertRaises(ParseException, mr.parse_string, "2t")
self.assertRaises(ParseException, mr.parse_string, char)
self.assertEqual("simple_test", mr.parse_string("simple_test")[0].name)
self.assertEqual("1066", bp.number.parse_string("1066")[0])
self.assertEqual("0", bp.number.parse_string("0")[0])
self.assertRaises(ParseException, bp.number.parse_string, "-4")
self.assertRaises(ParseException, bp.number.parse_string, "+4")
self.assertRaises(ParseException, bp.number.parse_string, ".4")
self.assertEqual("0", bp.number.parse_string("0.4")[0])
self.assertEqual(bp.chars_no_quotecurly.parse_string("x")[0], "x")
self.assertEqual(bp.chars_no_quotecurly.parse_string("a string")[0], "a string")
self.assertEqual(bp.chars_no_quotecurly.parse_string('a "string')[0], "a ")
self.assertEqual(bp.chars_no_curly.parse_string("x")[0], "x")
self.assertEqual(bp.chars_no_curly.parse_string("a string")[0], "a string")
self.assertEqual(bp.chars_no_curly.parse_string("a {string")[0], "a ")
self.assertEqual(bp.chars_no_curly.parse_string("a }string")[0], "a ")
self.assertEqual(obj.parse_string("{}").as_list(), [])
self.assertEqual(obj.parse_string('{a "string}')[0], 'a "string')
obj.parse_string("{a {nested} string}").as_list(),
obj.parse_string("{a {double {nested}} string}").as_list(),
self.assertEqual([], obj.parse_string('""').as_list())
self.assertEqual("a string", obj.parse_string('"a string"')[0])
obj.parse_string('"a {nested} string"').as_list(),
obj.parse_string('"a {double {nested}} string"').as_list(),
self.assertEqual(Macro("someascii"), bp.string.parse_string("someascii")[0])
self.assertRaises(ParseException, bp.string.parse_string, "%#= validstring")
self.assertEqual(bp.string.parse_string("1994")[0], "1994")
self.assertEqual(Macro("aname"), fv.parse_string("aname")[0])
self.assertEqual(Macro("aname"), fv.parse_string("ANAME")[0])
fv.parse_string('aname # "some string"').as_list(),
fv.parse_string("aname # {some {string}}").as_list(),
["a string", "1994"], fv.parse_string('"a string" # 1994').as_list()
fv.parse_string('"a string" # 1994 # a_macro').as_list(),
res = bp.comment.parse_string("@Comment{about something}")
self.assertEqual(res.as_list(), ["comment", "{about something}"])
bp.comment.parse_string("@COMMENT{about something").as_list(),
bp.comment.parse_string("@comment(about something").as_list(),
bp.comment.parse_string("@COMment about something").as_list(),
ParseException, bp.comment.parse_string, "@commentabout something"
ParseException, bp.comment.parse_string, "@comment+about something"
ParseException, bp.comment.parse_string, '@comment"about something'
res = bp.preamble.parse_string('@preamble{"about something"}')
self.assertEqual(res.as_list(), ["preamble", "about something"])
bp.preamble.parse_string("@PREamble{{about something}}").as_list(),
bp.preamble.parse_string(
).as_list(),
res = bp.macro.parse_string('@string{ANAME = "about something"}')
self.assertEqual(res.as_list(), ["string", "aname", "about something"])
bp.macro.parse_string("@string{aname = {about something}}").as_list(),
res = bp.entry.parse_string(txt)
res.as_list(),
res = bp.bibfile.parse_string(txt)
self.assertEqual(res.as_list(), res2.as_list())
res3 = [r.as_list()[0] for r, start, end in bp.definitions.scan_string(txt)]
self.assertEqual(res.as_list(), res3)
print(toks.as_list())
parsed = ppc.url.parse_string(url)
cpp_style_comment,
+ rest_of_line
Regex(r"\\\S+").set_parse_action(lambda t: t[0][1:]).set_name("escapedIdent")
) # .set_debug()
join_string=" ",
| (LPAR + Group(expr) + RPAR).set_name("nested_expr")
delay = Group("#" + delayArg).set_name("delay") # .set_debug()
stmt = Forward().set_name("stmt") # .set_debug()
verilogbnf.ignore(cpp_style_comment)
return ret.set_parse_action(pp.replace_with(val))
"""
failed = 0
for o, e in zip(orig.splitlines(), expected.splitlines()):
converted = pep8_converter.transform_string(o)
try:
assert converted == e
except AssertionError:
print()
print(f"Expected: {e!r}\n"
f"Observed: {converted!r}")
failed += 1
if failed:
raise AssertionError(f"Failed to convert some original code ({failed} lines)")

View File

@@ -0,0 +1,712 @@
#
# test_simple_unit.py
#
# While these unit tests *do* perform low-level unit testing of the classes in pyparsing,
# this testing module should also serve an instructional purpose, to clearly show simple passing
# and failing parse cases of some basic pyparsing expressions.
#
# Copyright (c) 2018 Paul T. McGuire
#
import unittest
from collections.abc import Iterable, Mapping
from datetime import datetime, timezone
from typing import NamedTuple
import pyparsing as pp
ppt = pp.pyparsing_test
TestParseResultsAsserts = ppt.TestParseResultsAsserts
# Test spec data class for specifying simple pyparsing test cases
class PyparsingTest(NamedTuple):
desc: str = ""
expr: pp.ParserElement = pp.Empty()
text: str = ""
parse_fn: str = "parse_string"
expected_list: Iterable = None
expected_dict: Mapping = None
expected_fail_locn: int = None
NL = "\n"
class PyparsingExpressionTestCase(ppt.TestParseResultsAsserts, unittest.TestCase):
"""
Base pyparsing testing class to parse various pyparsing expressions against
given text strings. Subclasses must define a class attribute 'tests' which
is a list of PyparsingTest instances.
"""
tests = []
def runTest(self):
if self.__class__ is PyparsingExpressionTestCase:
return
for test_spec in self.tests:
# for each spec in the class's tests list, create a subtest
# that will either:
# - parse the string with expected success, display the
# results, and validate the returned ParseResults
# - or parse the string with expected failure, display the
# error message and mark the error location, and validate
# the location against an expected value
with self.subTest(test_spec=test_spec):
test_spec.expr.streamline()
print(
f"{NL}{test_spec.desc} - {type(test_spec.expr).__name__}({test_spec.expr})"
)
parse_function = getattr(test_spec.expr, test_spec.parse_fn)
if test_spec.expected_fail_locn is None:
# expect success
subtest_result = parse_function(test_spec.text)
if test_spec.parse_fn == "parse_string":
print(subtest_result.dump())
# compare results against given list and/or dict
self.assertParseResultsEquals(
subtest_result,
expected_list=test_spec.expected_list,
expected_dict=test_spec.expected_dict,
)
elif test_spec.parse_fn == "transformString":
print(subtest_result)
# compare results against given list and/or dict
if test_spec.expected_list is not None:
self.assertEqual([subtest_result], test_spec.expected_list)
elif test_spec.parse_fn == "searchString":
print(subtest_result)
# compare results against given list and/or dict
if test_spec.expected_list is not None:
self.assertEqual([subtest_result], test_spec.expected_list)
else:
# expect fail
with self.assertRaisesParseException():
try:
parse_function(test_spec.text)
except pp.ParseBaseException as exc:
print(pp.ParseException.explain(exc))
self.assertEqual(exc.loc, test_spec.expected_fail_locn)
raise
# =========== TEST DEFINITIONS START HERE ==============
class TestLiteral(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Simple match",
expr=pp.Literal("xyz"),
text="xyz",
expected_list=["xyz"],
),
PyparsingTest(
desc="Simple match after skipping whitespace",
expr=pp.Literal("xyz"),
text=" xyz",
expected_list=["xyz"],
),
PyparsingTest(
desc="Simple fail - parse an empty string",
expr=pp.Literal("xyz"),
text="",
expected_fail_locn=0,
),
PyparsingTest(
desc="Simple fail - parse a mismatching string",
expr=pp.Literal("xyz"),
text="xyu",
expected_fail_locn=0,
),
PyparsingTest(
desc="Simple fail - parse a partially matching string",
expr=pp.Literal("xyz"),
text="xy",
expected_fail_locn=0,
),
PyparsingTest(
desc="Fail - parse a partially matching string by matching individual letters",
expr=pp.Literal("x") + pp.Literal("y") + pp.Literal("z"),
text="xy",
expected_fail_locn=2,
),
]
class TestCaselessLiteral(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Match colors, converting to consistent case",
expr=(
pp.CaselessLiteral("RED")
| pp.CaselessLiteral("GREEN")
| pp.CaselessLiteral("BLUE")
)[...],
text="red Green BluE blue GREEN green rEd",
expected_list=["RED", "GREEN", "BLUE", "BLUE", "GREEN", "GREEN", "RED"],
),
]
class TestWord(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Simple Word match",
expr=pp.Word("xy"),
text="xxyxxyy",
expected_list=["xxyxxyy"],
),
PyparsingTest(
desc="Simple Word match of two separate Words",
expr=pp.Word("x") + pp.Word("y"),
text="xxxxxyy",
expected_list=["xxxxx", "yy"],
),
PyparsingTest(
desc="Simple Word match of two separate Words - implicitly skips whitespace",
expr=pp.Word("x") + pp.Word("y"),
text="xxxxx yy",
expected_list=["xxxxx", "yy"],
),
]
class TestCombine(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Parsing real numbers - fail, parsed numbers are in pieces",
expr=(pp.Word(pp.nums) + "." + pp.Word(pp.nums))[...],
text="1.2 2.3 3.1416 98.6",
# fmt: off
expected_list=["1", ".", "2", "2", ".", "3", "3", ".", "1416", "98", ".", "6"],
# fmt: on
),
PyparsingTest(
desc="Parsing real numbers - better, use Combine to combine multiple tokens into one",
expr=pp.Combine(pp.Word(pp.nums) + "." + pp.Word(pp.nums))[...],
text="1.2 2.3 3.1416 98.6",
expected_list=["1.2", "2.3", "3.1416", "98.6"],
),
]
class TestRepetition(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Match several words",
expr=(pp.Word("x") | pp.Word("y"))[...],
text="xxyxxyyxxyxyxxxy",
expected_list=["xx", "y", "xx", "yy", "xx", "y", "x", "y", "xxx", "y"],
),
PyparsingTest(
desc="Match several words, skipping whitespace",
expr=(pp.Word("x") | pp.Word("y"))[...],
text="x x y xxy yxx y xyx xxy",
# fmt: off
expected_list=["x", "x", "y", "xx", "y", "y", "xx", "y", "x", "y", "x", "xx", "y"],
# fmt: on
),
PyparsingTest(
desc="Match several words, skipping whitespace (old style)",
expr=pp.OneOrMore(pp.Word("x") | pp.Word("y")),
text="x x y xxy yxx y xyx xxy",
# fmt: off
expected_list=["x", "x", "y", "xx", "y", "y", "xx", "y", "x", "y", "x", "xx", "y"],
# fmt: on
),
PyparsingTest(
desc="Match words and numbers - show use of results names to collect types of tokens",
expr=(pp.Word(pp.alphas)("alpha*") | pp.pyparsing_common.integer("int*"))[
...
],
text="sdlfj23084ksdfs08234kjsdlfkjd0934",
expected_list=["sdlfj", 23084, "ksdfs", 8234, "kjsdlfkjd", 934],
expected_dict={
"alpha": ["sdlfj", "ksdfs", "kjsdlfkjd"],
"int": [23084, 8234, 934],
},
),
PyparsingTest(
desc="Using DelimitedList (comma is the default delimiter)",
expr=pp.DelimitedList(pp.Word(pp.alphas)),
text="xxyx,xy,y,xxyx,yxx, xy",
expected_list=["xxyx", "xy", "y", "xxyx", "yxx", "xy"],
),
PyparsingTest(
desc="Using DelimitedList (comma is the default delimiter) with trailing delimiter",
expr=pp.DelimitedList(pp.Word(pp.alphas), allow_trailing_delim=True),
text="xxyx,xy,y,xxyx,yxx, xy,",
expected_list=["xxyx", "xy", "y", "xxyx", "yxx", "xy"],
),
PyparsingTest(
desc="Using DelimitedList (comma is the default delimiter) with minimum size",
expr=pp.DelimitedList(pp.Word(pp.alphas), min=3),
text="xxyx,xy",
expected_fail_locn=7,
),
PyparsingTest(
desc="Using DelimitedList (comma is the default delimiter) with maximum size",
expr=pp.DelimitedList(pp.Word(pp.alphas), max=3),
text="xxyx,xy,y,xxyx,yxx, xy,",
expected_list=["xxyx", "xy", "y"],
),
PyparsingTest(
desc="Using DelimitedList, with ':' delimiter",
expr=pp.DelimitedList(
pp.Word(pp.hexnums, exact=2), delim=":", combine=True
),
text="0A:4B:73:21:FE:76",
expected_list=["0A:4B:73:21:FE:76"],
),
PyparsingTest(
desc="Using DelimitedList, with ':' delimiter",
expr=pp.DelimitedList(
pp.Word(pp.hexnums, exact=2),
delim=":",
combine=True,
allow_trailing_delim=True,
),
text="0A:4B:73:21:FE:76:",
expected_list=["0A:4B:73:21:FE:76:"],
),
]
class TestResultsName(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Match with results name",
expr=pp.Literal("xyz").set_results_name("value"),
text="xyz",
expected_dict={"value": "xyz"},
expected_list=["xyz"],
),
PyparsingTest(
desc="Match with results name - using naming short-cut",
expr=pp.Literal("xyz")("value"),
text="xyz",
expected_dict={"value": "xyz"},
expected_list=["xyz"],
),
PyparsingTest(
desc="Define multiple results names",
expr=pp.Word(pp.alphas, pp.alphanums)("key")
+ "="
+ pp.pyparsing_common.integer("value"),
text="range=5280",
expected_dict={"key": "range", "value": 5280},
expected_list=["range", "=", 5280],
),
]
class TestGroups(PyparsingExpressionTestCase):
EQ = pp.Suppress("=")
tests = [
PyparsingTest(
desc="Define multiple results names in groups",
expr=pp.Group(
pp.Word(pp.alphas)("key") + EQ + pp.pyparsing_common.number("value")
)[...],
text="range=5280 long=-138.52 lat=46.91",
expected_list=[["range", 5280], ["long", -138.52], ["lat", 46.91]],
),
PyparsingTest(
desc=(
"Define multiple results names in groups"
" - use Dict to define results names using parsed keys"
),
expr=pp.Dict(
pp.Group(pp.Word(pp.alphas) + EQ + pp.pyparsing_common.number)[...]
),
text="range=5280 long=-138.52 lat=46.91",
expected_list=[["range", 5280], ["long", -138.52], ["lat", 46.91]],
expected_dict={"lat": 46.91, "long": -138.52, "range": 5280},
),
PyparsingTest(
desc="Define multiple value types",
expr=pp.Dict(
pp.Group(
pp.Word(pp.alphas)
+ EQ
+ (
pp.pyparsing_common.number
| pp.oneOf("True False")
| pp.QuotedString("'")
)
)[...]
),
text="long=-122.47 lat=37.82 public=True name='Golden Gate Bridge'",
expected_list=[
["long", -122.47],
["lat", 37.82],
["public", "True"],
["name", "Golden Gate Bridge"],
],
expected_dict={
"long": -122.47,
"lat": 37.82,
"public": "True",
"name": "Golden Gate Bridge",
},
),
]
class TestParseAction(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Parsing real numbers - use parse action to convert to float at parse time",
expr=pp.Combine(pp.Word(pp.nums) + "." + pp.Word(pp.nums)).add_parse_action(
lambda t: float(t[0])
)[...],
text="1.2 2.3 3.1416 98.6",
# note, these are now floats, not strs
expected_list=[1.2, 2.3, 3.1416, 98.6],
),
PyparsingTest(
desc="Match with numeric string converted to int",
expr=pp.Word("0123456789").addParseAction(lambda t: int(t[0])),
text="12345",
expected_list=[12345], # note - result is type int, not str
),
PyparsingTest(
desc="Use two parse actions to convert numeric string, then convert to datetime",
expr=pp.Word(pp.nums).add_parse_action(
lambda t: int(t[0]),
lambda t: datetime.fromtimestamp(t[0], timezone.utc),
),
text="1537415628",
expected_list=[datetime(2018, 9, 20, 3, 53, 48, tzinfo=timezone.utc)],
),
PyparsingTest(
desc="Use tokenMap for parse actions that operate on a single-length token",
expr=pp.Word(pp.nums).add_parse_action(
pp.token_map(int),
pp.token_map(lambda t: datetime.fromtimestamp(t, timezone.utc)),
),
text="1537415628",
expected_list=[datetime(2018, 9, 20, 3, 53, 48, tzinfo=timezone.utc)],
),
PyparsingTest(
desc="Using a built-in function that takes a sequence of strs as a parse action",
expr=pp.Word(pp.hexnums, exact=2)[...].add_parse_action(":".join),
text="0A4B7321FE76",
expected_list=["0A:4B:73:21:FE:76"],
),
PyparsingTest(
desc="Using a built-in function that takes a sequence of strs as a parse action",
expr=pp.Word(pp.hexnums, exact=2)[...].add_parse_action(sorted),
text="0A4B7321FE76",
expected_list=["0A", "21", "4B", "73", "76", "FE"],
),
]
class TestResultsModifyingParseAction(PyparsingExpressionTestCase):
# do not make staticmethod
# @staticmethod
def compute_stats_parse_action(t):
# by the time this parse action is called, parsed numeric words
# have been converted to ints by a previous parse action, so
# they can be treated as ints
t["sum"] = sum(t)
t["ave"] = sum(t) / len(t)
t["min"] = min(t)
t["max"] = max(t)
tests = [
PyparsingTest(
desc="A parse action that adds new key-values",
expr=pp.pyparsing_common.integer[...].addParseAction(
compute_stats_parse_action
),
text="27 1 14 22 89",
expected_list=[27, 1, 14, 22, 89],
expected_dict={"ave": 30.6, "max": 89, "min": 1, "sum": 153},
),
]
class TestRegex(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Parsing real numbers - using Regex instead of Combine",
expr=pp.Regex(r"\d+\.\d+").add_parse_action(lambda t: float(t[0]))[...],
text="1.2 2.3 3.1416 98.6",
# note, these are now floats, not strs
expected_list=[1.2, 2.3, 3.1416, 98.6],
),
]
class TestParseCondition(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="Define a condition to only match numeric values that are multiples of 7",
expr=pp.Word(pp.nums).addCondition(lambda t: int(t[0]) % 7 == 0)[...],
text="14 35 77 12 28",
expected_list=["14", "35", "77"],
),
PyparsingTest(
desc="Separate conversion to int and condition into separate parse action/conditions",
expr=pp.Word(pp.nums)
.add_parse_action(lambda t: int(t[0]))
.add_condition(lambda t: t[0] % 7 == 0)[...],
text="14 35 77 12 28",
expected_list=[14, 35, 77],
),
]
class TestTransformStringUsingParseActions(PyparsingExpressionTestCase):
markup_convert_map = {
"*": "B",
"_": "U",
"/": "I",
}
# do not make staticmethod
# @staticmethod
def markup_convert(t):
htmltag = TestTransformStringUsingParseActions.markup_convert_map[
t.markup_symbol
]
return f"<{htmltag}>{t.body}</{htmltag}>"
tests = [
PyparsingTest(
desc="Use transformString to convert simple markup to HTML",
expr=(
pp.one_of(markup_convert_map)("markup_symbol")
+ "("
+ pp.CharsNotIn(")")("body")
+ ")"
).add_parse_action(markup_convert),
text="Show in *(bold), _(underscore), or /(italic) type",
expected_list=[
"Show in <B>bold</B>, <U>underscore</U>, or <I>italic</I> type"
],
parse_fn="transformString",
),
]
class TestCommonHelperExpressions(PyparsingExpressionTestCase):
tests = [
PyparsingTest(
desc="A comma-delimited list of words",
expr=pp.DelimitedList(pp.Word(pp.alphas)),
text="this, that, blah,foo, bar",
expected_list=["this", "that", "blah", "foo", "bar"],
),
PyparsingTest(
desc="A counted array of words",
expr=pp.Group(pp.counted_array(pp.Word("ab")))[...],
text="2 aaa bbb 0 3 abab bbaa abbab",
expected_list=[["aaa", "bbb"], [], ["abab", "bbaa", "abbab"]],
),
PyparsingTest(
desc="skipping comments with ignore",
expr=(
pp.pyparsing_common.identifier("lhs")
+ "="
+ pp.pyparsing_common.fnumber("rhs")
).ignore(pp.cpp_style_comment),
text="abc_100 = /* value to be tested */ 3.1416",
expected_list=["abc_100", "=", 3.1416],
expected_dict={"lhs": "abc_100", "rhs": 3.1416},
),
PyparsingTest(
desc=(
"some pre-defined expressions in pyparsing_common, and"
" building a dotted identifier with DelimitedList"
),
expr=(
pp.pyparsing_common.number("id_num")
+ pp.DelimitedList(pp.pyparsing_common.identifier, ".", combine=True)(
"name"
)
+ pp.pyparsing_common.ipv4_address("ip_address")
),
text="1001 www.google.com 192.168.10.199",
expected_list=[1001, "www.google.com", "192.168.10.199"],
expected_dict={
"id_num": 1001,
"name": "www.google.com",
"ip_address": "192.168.10.199",
},
),
PyparsingTest(
desc="using one_of (shortcut for Literal('a') | Literal('b') | Literal('c'))",
expr=pp.one_of("a b c")[...],
text="a b a b b a c c a b b",
expected_list=["a", "b", "a", "b", "b", "a", "c", "c", "a", "b", "b"],
),
PyparsingTest(
desc="parsing nested parentheses",
expr=pp.nested_expr(),
text="(a b (c) d (e f g ()))",
expected_list=[["a", "b", ["c"], "d", ["e", "f", "g", []]]],
),
PyparsingTest(
desc="parsing nested braces",
expr=(
pp.Keyword("if")
+ pp.nested_expr()("condition")
+ pp.nested_expr("{", "}")("body")
),
text='if ((x == y) || !z) {printf("{}");}',
expected_list=[
"if",
[["x", "==", "y"], "||", "!z"],
["printf(", '"{}"', ");"],
],
expected_dict={
"condition": [[["x", "==", "y"], "||", "!z"]],
"body": [["printf(", '"{}"', ");"]],
},
),
]
class TestWhitespaceMethods(PyparsingExpressionTestCase):
tests = [
# These test the single-element versions
PyparsingTest(
desc="The word foo",
expr=pp.Literal("foo").ignore_whitespace(),
text=" foo ",
expected_list=["foo"],
),
PyparsingTest(
desc="The word foo",
expr=pp.Literal("foo").leave_whitespace(),
text=" foo ",
expected_fail_locn=0,
),
PyparsingTest(
desc="The word foo",
expr=pp.Literal("foo").ignore_whitespace(),
text="foo",
expected_list=["foo"],
),
PyparsingTest(
desc="The word foo",
expr=pp.Literal("foo").leave_whitespace(),
text="foo",
expected_list=["foo"],
),
# These test the composite elements
PyparsingTest(
desc=(
"If we recursively leave whitespace on the parent, this"
" whitespace-dependent grammar will succeed, even if the"
" children themselves skip whitespace"
),
expr=pp.And(
[
pp.Literal(" foo").ignore_whitespace(),
pp.Literal(" bar").ignore_whitespace(),
]
).leave_whitespace(recursive=True),
text=" foo bar",
expected_list=[" foo", " bar"],
),
#
PyparsingTest(
desc=(
"If we recursively ignore whitespace in our parsing, this"
" whitespace-dependent grammar will fail, even if the children"
" themselves keep whitespace"
),
expr=pp.And(
[
pp.Literal(" foo").leave_whitespace(),
pp.Literal(" bar").leave_whitespace(),
]
).ignore_whitespace(recursive=True),
text=" foo bar",
expected_fail_locn=1,
),
PyparsingTest(
desc=(
"If we leave whitespace on the parent, but it isn't recursive,"
" this whitespace-dependent grammar will fail"
),
expr=pp.And(
[
pp.Literal(" foo").ignore_whitespace(),
pp.Literal(" bar").ignore_whitespace(),
]
).leave_whitespace(recursive=False),
text=" foo bar",
expected_fail_locn=5,
),
# These test the Enhance classes
PyparsingTest(
desc=(
"If we recursively leave whitespace on the parent,"
" this whitespace-dependent grammar will succeed, even"
" if the children themselves skip whitespace"
),
expr=pp.Optional(pp.Literal(" foo").ignore_whitespace()).leave_whitespace(
recursive=True
),
text=" foo",
expected_list=[" foo"],
),
#
PyparsingTest(
desc=(
"If we ignore whitespace on the parent, but it isn't recursive,"
" parsing will fail because we skip to the first character"
" 'f' before the internal expr can see it"
),
expr=pp.Optional(pp.Literal(" foo").leave_whitespace()).ignore_whitespace(
recursive=True
),
text=" foo",
expected_list=[],
),
# PyparsingTest(
# desc=(
# "If we leave whitespace on the parent, this whitespace-dependent"
# " grammar will succeed, even if the children themselves skip whitespace"
# ),
# expr=pp.Optional(pp.Literal(" foo").ignoreWhitespace()).leaveWhitespace(
# recursive=False
# ),
# text=" foo",
# expected_list=[]
# ),
]
def _get_decl_line_no(cls):
import inspect
return inspect.getsourcelines(cls)[1]
# get all test case classes defined in this module and sort them by decl line no
test_case_classes = list(PyparsingExpressionTestCase.__subclasses__())
test_case_classes.sort(key=_get_decl_line_no)
# make into a suite and run it - this will run the tests in the same order
# they are declared in this module
#
# runnable from setup.py using "python setup.py test -s simple_unit_tests.suite"
#
suite = unittest.TestSuite(cls() for cls in test_case_classes)
# ============ MAIN ================
if __name__ == "__main__":
result = unittest.TextTestRunner().run(suite)
exit(0 if result.wasSuccessful() else 1)

View File

@@ -0,0 +1,244 @@
import random
from typing import Union, Iterable
import pytest
import pyparsing as pp
@pytest.mark.parametrize(
"test_label, input_list, expected_output",
[
("Empty list", [], []),
("Flat list", [1, 2, 3], [1, 2, 3]),
("Nested list", [[1, 2], [3, 4]], [1, 2, 3, 4]),
("Mixed list with single values and lists", [1, [2, 3], 4], [1, 2, 3, 4]),
("Deeper nested lists", [1, [2, [3, 4]], 5], [1, 2, 3, 4, 5]),
("Deeper nesting with sublists", [[[1], 2], [3]], [1, 2, 3]),
(
"Mixed empty and non-empty nested lists",
[[], [1], [2, [3]], [[4, 5]]],
[1, 2, 3, 4, 5],
),
("Deeply nested empty lists", [[[[]]]], []),
("Mixed empty lists and non-empty elements", [1, [], 2, [3, []]], [1, 2, 3]),
(
"ParseResults instead of lists",
[pp.ParseResults([1, 2]), pp.ParseResults([3, 4])],
[1, 2, 3, 4],
),
(
"ParseResults with mixed types",
[1, pp.ParseResults([2, 3]), 4],
[1, 2, 3, 4],
),
(
"Nested ParseResults",
[pp.ParseResults([1, pp.ParseResults([2, 3])]), 4],
[1, 2, 3, 4],
),
(
"Empty ParseResults",
[pp.ParseResults([]), 1, pp.ParseResults([2, 3])],
[1, 2, 3],
),
],
)
def test_flatten(test_label: str, input_list: list, expected_output: list):
from pyparsing.util import _flatten
"""Test flatten with various inputs."""
print(test_label)
assert _flatten(input_list) == expected_output
@pytest.mark.parametrize(
"test_label, input_string, re_escape, expected_output",
[
("Empty string", "", True, ""),
("Single character", "a", True, "a"),
("Two consecutive characters", "ab", True, "ab"),
("Two non-consecutive characters", "az", True, "az"),
("Three consecutive characters", "abcg", True, "a-cg"),
("Full consecutive alphabet", "abcdefghijklmnopqrstuvwxyz", True, "a-z"),
("Consecutive characters with special regex chars", "^-]", True, r"\-\]\^"),
("Consecutive characters without escaping", "^-]", False, "-]^"),
("Mixed consecutive and non-consecutive", "abcxyz", True, "a-cx-z"),
("Reversed order input", "cba", True, "a-c"),
("Characters with duplicates", "aaxxxddddbbcc", True, "a-dx"),
("Non-alphabetic consecutive characters", "012345", True, "0-5"),
("Non-alphabetic mixed characters", "@05", True, "05@"),
("Non-alphabetic, non-consecutive characters", "02", True, "02"),
("Verify range ending with '-' creates valid regex", "*+,-", True, r"*-\-"),
("Verify range ending with ']' creates valid regex", r"WXYZ[\]", True, r"W-\]"),
("Verify range starting with '^' creates valid regex", "^_`a", True, r"\^-a"),
],
)
def test_collapse_string_to_ranges(
test_label: str,
input_string: Union[str, Iterable[str]],
re_escape: bool,
expected_output: str,
):
"""Test collapsing a string into character ranges with and without regex escaping."""
from pyparsing.util import _collapse_string_to_ranges
from random import random
import re
print(test_label)
collapsed = _collapse_string_to_ranges(input_string, re_escape)
print(f"{input_string!r} -> {collapsed!r}")
assert collapsed == expected_output
if input_string:
# for added coverage, randomly shuffle input string
shuffled = "".join(sorted(list(input_string), key=lambda _: random()))
collapsed = _collapse_string_to_ranges(shuffled, re_escape)
print(f"{shuffled!r} -> {collapsed!r}")
assert collapsed == expected_output
# added test to assert that the collapsed string can be used as a valid regex range string
if re_escape:
collapsed_re = re.compile(f"[{collapsed}]+")
match = collapsed_re.match(input_string)
print(f"re.match:'{collapsed_re.pattern}' -> {match and match[0]!r}")
assert match is not None and match[0] == input_string
print()
@pytest.mark.parametrize(
"test_label, loc, input_string, expected_output",
[
("First column, no newline", 0, "abcdef", 1),
("Second column, no newline", 1, "abcdef", 2),
("First column after newline", 4, "abc\ndef", 1),
("Second column after newline", 5, "abc\ndef", 2),
("Column after multiple newlines", 9, "abc\ndef\nghi", 2),
("Location at start of string", 0, "abcdef", 1),
("Location at end of string", 5, "abcdef", 6),
("Column after newline at end", 3, "abc\n", 4),
("Tab character in the string", 4, "a\tbcd\tef", 5),
("Multiple lines with tab", 8, "a\tb\nc\td", 5),
],
)
def test_col(test_label: str, loc: int, input_string: str, expected_output: int):
from pyparsing.util import col
print(test_label)
assert col(loc, input_string) == expected_output
@pytest.mark.parametrize(
"test_label, loc, input_string, expected_output",
[
("Single line, no newlines", 0, "abcdef", "abcdef"),
("First line in multi-line string", 2, "abc\ndef", "abc"),
("Second line in multi-line string", 5, "abc\ndef", "def"),
("Location at start of second line", 4, "abc\ndef", "def"),
("Empty string", 0, "", ""),
("Location at newline character", 3, "abc\ndef", "abc"),
("Last line without trailing newline", 7, "abc\ndef\nghi", "def"),
("Single line with newline at end", 2, "abc\n", "abc"),
("Multi-line with multiple newlines", 6, "line1\nline2\nline3", "line2"),
("Multi-line with trailing newline", 11, "line1\nline2\nline3\n", "line2"),
],
)
def test_line(test_label: str, loc: int, input_string: str, expected_output: str):
from pyparsing import line
print(test_label)
assert line(loc, input_string) == expected_output
@pytest.mark.parametrize(
"test_label, loc, input_string, expected_output",
[
("Single line, no newlines", 0, "abcdef", 1),
("First line in multi-line string", 2, "abc\ndef", 1),
("Second line in multi-line string", 5, "abc\ndef", 2),
("Location at start of second line", 4, "abc\ndef", 2),
("Multiple newlines, third line", 10, "abc\ndef\nghi", 3),
("Empty string", 0, "", 1),
("Location at newline character", 3, "abc\ndef", 1),
("Last line without trailing newline", 7, "abc\ndef\nghi", 2),
("Single line with newline at end", 4, "abc\n", 2),
("Multi-line with trailing newline", 12, "line1\nline2\nline3\n", 3),
("Location in middle of a tabbed string", 7, "a\tb\nc\td", 2),
],
)
def test_lineno(test_label: str, loc: int, input_string: str, expected_output: int):
from pyparsing import lineno
assert lineno(loc, input_string) == expected_output
def test_html_entities() -> None:
import html.entities
from pyparsing import common_html_entity
# create test string from all known HTML5 entities, in random order
entity_strings = [f'&{e.rstrip(";")};' for e in html.entities.html5]
random.shuffle(entity_strings)
test_string = " ".join(entity_strings)
# verify that all are parsed
parsed = common_html_entity()[...].parse_string(test_string, parse_all=True)
assert len(parsed) == len(html.entities.html5)
def test_make_compressed_re() -> None:
import re
from pyparsing.util import make_compressed_re
words = "blue brown black blues bluesky co ci ce ca cu".split()
for i in range(1, 9):
print(i, make_compressed_re(words, max_level=i))
regex = re.compile(make_compressed_re(words, max_level=i) + "$")
assert all(regex.match(wd) for wd in words)
def test_make_compressed_re_bad_input():
from pyparsing.util import make_compressed_re
with pytest.raises(ValueError):
make_compressed_re([])
with pytest.raises(ValueError):
make_compressed_re(["a", "", "b", "c"])
# handle duplicate input strings
assert make_compressed_re(["a", "b", "c"]) == make_compressed_re(["a", "b", "c", "a"])
def test_make_compressed_re_random():
import itertools
import re
from pyparsing.util import make_compressed_re
def generate_random_word(max_length: int) -> str:
import random
import string
length = random.randint(1, max_length)
return ''.join(random.choice(string.ascii_lowercase + ".*? ") for _ in range(length))
def generate_word_lists(num_lists: int, num_words: int, word_length: int) -> Iterable[list[str]]:
yield from (
[generate_random_word(word_length) for _ in range(num_words)]
for _ in range(num_lists)
)
for word_length, list_length in itertools.product(range(3, 9), range(1, 32)):
for word_list in generate_word_lists(100, list_length, word_length):
regex_pattern = make_compressed_re(word_list)
try:
regex = re.compile(f"^({regex_pattern})$")
except Exception as e:
assert False, f"Failed to compile {word_list} to regex pattern {regex_pattern!r}: {e}"
for word in word_list:
assert regex.match(word), f"Regex {regex_pattern!r} did not match word: {word}"
# Check that the regex does not match a random word not in the list
random_word = generate_random_word(word_length)
while random_word in word_list:
random_word = generate_random_word(word_length)
assert regex.match(random_word) is None, f"Regex {regex_pattern!r} incorrectly matched word: {random_word!r}"