summaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/pg8000/.dist-info/METADATA181
-rw-r--r--contrib/python/pg8000/README.md177
-rw-r--r--contrib/python/pg8000/pg8000/converters.py2
-rw-r--r--contrib/python/pg8000/pg8000/core.py2
-rw-r--r--contrib/python/pg8000/ya.make2
-rw-r--r--contrib/python/pyparsing/py3/.dist-info/METADATA9
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/__init__.py32
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/actions.py168
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/common.py143
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/core.py1251
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/diagram/__init__.py44
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/exceptions.py33
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/helpers.py587
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/results.py474
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/testing.py96
-rw-r--r--contrib/python/pyparsing/py3/pyparsing/util.py48
-rw-r--r--contrib/python/pyparsing/py3/ya.make2
17 files changed, 2194 insertions, 1057 deletions
diff --git a/contrib/python/pg8000/.dist-info/METADATA b/contrib/python/pg8000/.dist-info/METADATA
index b8e7e656413..2378ba7a3f5 100644
--- a/contrib/python/pg8000/.dist-info/METADATA
+++ b/contrib/python/pg8000/.dist-info/METADATA
@@ -1,8 +1,8 @@
Metadata-Version: 2.4
Name: pg8000
-Version: 1.31.4
+Version: 1.31.5
Summary: PostgreSQL interface library
-Project-URL: Homepage, https://github.com/tlocke/pg8000
+Project-URL: Homepage, https://codeberg.org/tlocke/pg8000
Author: The Contributors
License: BSD 3-Clause License
License-File: LICENSE
@@ -36,10 +36,172 @@ pg8000 is a pure-[Python](https://www.python.org/)
belief that it is probably about the 8000th PostgreSQL interface for Python. pg8000 is
distributed under the BSD 3-clause license.
-All bug reports, feature requests and contributions are welcome at
-[http://github.com/tlocke/pg8000/](http://github.com/tlocke/pg8000/).
+<!-- mtoc-start -->
-[![Workflow Status Badge](https://github.com/tlocke/pg8000/actions/workflows/test.yml/badge.svg)](https://github.com/tlocke/pg8000/actions/workflows/test.yml)
+* [Installation](#installation)
+* [Native API Interactive Examples](#native-api-interactive-examples)
+ * [Basic Example](#basic-example)
+ * [Transactions](#transactions)
+ * [Query Using Functions](#query-using-functions)
+ * [Interval Type](#interval-type)
+ * [Point Type](#point-type)
+ * [Client Encoding](#client-encoding)
+ * [JSON](#json)
+ * [Retrieve Column Metadata From Results](#retrieve-column-metadata-from-results)
+ * [Notices And Notifications](#notices-and-notifications)
+ * [Parameter Statuses](#parameter-statuses)
+ * [LIMIT ALL](#limit-all)
+ * [IN and NOT IN](#in-and-not-in)
+ * [Many SQL Statements Can't Be Parameterized](#many-sql-statements-cant-be-parameterized)
+ * [COPY FROM And TO A Stream](#copy-from-and-to-a-stream)
+ * [Execute Multiple SQL Statements](#execute-multiple-sql-statements)
+ * [Quoted Identifiers in SQL](#quoted-identifiers-in-sql)
+ * [Custom adapter from a Python type to a PostgreSQL type](#custom-adapter-from-a-python-type-to-a-postgresql-type)
+ * [Custom adapter from a PostgreSQL type to a Python type](#custom-adapter-from-a-postgresql-type-to-a-python-type)
+ * [Could Not Determine Data Type Of Parameter](#could-not-determine-data-type-of-parameter)
+ * [Prepared Statements](#prepared-statements)
+ * [Use Environment Variables As Connection Defaults](#use-environment-variables-as-connection-defaults)
+ * [Connect To PostgreSQL Over SSL](#connect-to-postgresql-over-ssl)
+ * [Server-Side Cursors](#server-side-cursors)
+ * [BLOBs (Binary Large Objects)](#blobs-binary-large-objects)
+ * [Replication Protocol](#replication-protocol)
+ * [Extra Startup Parameters](#extra-startup-parameters)
+ * [PostgreSQL-Style Parameter Placeholders](#postgresql-style-parameter-placeholders)
+* [DB-API 2 Interactive Examples](#db-api-2-interactive-examples)
+ * [Basic Example](#basic-example-1)
+ * [Query Using Functions](#query-using-functions-1)
+ * [Interval Type](#interval-type-1)
+ * [Point Type](#point-type-1)
+ * [Numeric Parameter Style](#numeric-parameter-style)
+ * [Autocommit](#autocommit)
+ * [Client Encoding](#client-encoding-1)
+ * [JSON](#json-1)
+ * [Retrieve Column Names From Results](#retrieve-column-names-from-results)
+ * [COPY from and to a file](#copy-from-and-to-a-file)
+ * [Server-Side Cursors](#server-side-cursors-1)
+ * [BLOBs (Binary Large Objects)](#blobs-binary-large-objects-1)
+ * [Parameter Limit](#parameter-limit)
+* [Type Mapping](#type-mapping)
+* [Theory Of Operation](#theory-of-operation)
+* [Native API Docs](#native-api-docs)
+ * [pg8000.native.Error](#pg8000nativeerror)
+ * [pg8000.native.InterfaceError](#pg8000nativeinterfaceerror)
+ * [pg8000.native.DatabaseError](#pg8000nativedatabaseerror)
+ * [pg8000.native.Connection(user, host='localhost', database=None, port=5432, password=None, source\_address=None, unix\_sock=None, ssl\_context=None, timeout=None, tcp\_keepalive=True, application\_name=None, replication=None, sock=None)](#pg8000nativeconnectionuser-hostlocalhost-databasenone-port5432-passwordnone-source_addressnone-unix_socknone-ssl_contextnone-timeoutnone-tcp_keepalivetrue-application_namenone-replicationnone-socknone)
+ * [pg8000.native.Connection.notifications](#pg8000nativeconnectionnotifications)
+ * [pg8000.native.Connection.notices](#pg8000nativeconnectionnotices)
+ * [pg8000.native.Connection.parameter\_statuses](#pg8000nativeconnectionparameter_statuses)
+ * [pg8000.native.Connection.run(sql, stream=None, types=None, \*\*kwargs)](#pg8000nativeconnectionrunsql-streamnone-typesnone-kwargs)
+ * [pg8000.native.Connection.row\_count](#pg8000nativeconnectionrow_count)
+ * [pg8000.native.Connection.columns](#pg8000nativeconnectioncolumns)
+ * [pg8000.native.Connection.close()](#pg8000nativeconnectionclose)
+ * [pg8000.native.Connection.register\_out\_adapter(typ, out\_func)](#pg8000nativeconnectionregister_out_adaptertyp-out_func)
+ * [pg8000.native.Connection.register\_in\_adapter(oid, in\_func)](#pg8000nativeconnectionregister_in_adapteroid-in_func)
+ * [pg8000.native.Connection.prepare(sql)](#pg8000nativeconnectionpreparesql)
+ * [pg8000.native.PreparedStatement](#pg8000nativepreparedstatement)
+ * [pg8000.native.PreparedStatement.run(\*\*kwargs)](#pg8000nativepreparedstatementrunkwargs)
+ * [pg8000.native.PreparedStatement.close()](#pg8000nativepreparedstatementclose)
+ * [pg8000.native.identifier(ident)](#pg8000nativeidentifierident)
+ * [pg8000.native.literal(value)](#pg8000nativeliteralvalue)
+* [DB-API 2 Docs](#db-api-2-docs)
+ * [Properties](#properties)
+ * [pg8000.dbapi.apilevel](#pg8000dbapiapilevel)
+ * [pg8000.dbapi.threadsafety](#pg8000dbapithreadsafety)
+ * [pg8000.dbapi.paramstyle](#pg8000dbapiparamstyle)
+ * [pg8000.dbapi.STRING](#pg8000dbapistring)
+ * [pg8000.dbapi.BINARY](#pg8000dbapibinary)
+ * [pg8000.dbapi.NUMBER](#pg8000dbapinumber)
+ * [pg8000.dbapi.DATETIME](#pg8000dbapidatetime)
+ * [pg8000.dbapi.ROWID](#pg8000dbapirowid)
+ * [Functions](#functions)
+ * [pg8000.dbapi.connect(user, host='localhost', database=None, port=5432, password=None, source\_address=None, unix\_sock=None, ssl\_context=None, timeout=None, tcp\_keepalive=True, applicationa_name=None, replication=None, sock=None)](#pg8000dbapiconnectuser-hostlocalhost-databasenone-port5432-passwordnone-source_addressnone-unix_socknone-ssl_contextnone-timeoutnone-tcp_keepalivetrue-applicationa_namenone-replicationnone-socknone)
+ * [pg8000.dbapi.Date(year, month, day)](#pg8000dbapidateyear-month-day)
+ * [pg8000.dbapi.Time(hour, minute, second)](#pg8000dbapitimehour-minute-second)
+ * [pg8000.dbapi.Timestamp(year, month, day, hour, minute, second)](#pg8000dbapitimestampyear-month-day-hour-minute-second)
+ * [pg8000.dbapi.DateFromTicks(ticks)](#pg8000dbapidatefromticksticks)
+ * [pg8000.dbapi.TimeFromTicks(ticks)](#pg8000dbapitimefromticksticks)
+ * [pg8000.dbapi.TimestampFromTicks(ticks)](#pg8000dbapitimestampfromticksticks)
+ * [pg8000.dbapi.Binary(value)](#pg8000dbapibinaryvalue)
+ * [Generic Exceptions](#generic-exceptions)
+ * [pg8000.dbapi.Warning](#pg8000dbapiwarning)
+ * [pg8000.dbapi.Error](#pg8000dbapierror)
+ * [pg8000.dbapi.InterfaceError](#pg8000dbapiinterfaceerror)
+ * [pg8000.dbapi.DatabaseError](#pg8000dbapidatabaseerror)
+ * [pg8000.dbapi.DataError](#pg8000dbapidataerror)
+ * [pg8000.dbapi.OperationalError](#pg8000dbapioperationalerror)
+ * [pg8000.dbapi.IntegrityError](#pg8000dbapiintegrityerror)
+ * [pg8000.dbapi.InternalError](#pg8000dbapiinternalerror)
+ * [pg8000.dbapi.ProgrammingError](#pg8000dbapiprogrammingerror)
+ * [pg8000.dbapi.NotSupportedError](#pg8000dbapinotsupportederror)
+ * [Classes](#classes)
+ * [pg8000.dbapi.Connection](#pg8000dbapiconnection)
+ * [pg8000.dbapi.Connection.autocommit](#pg8000dbapiconnectionautocommit)
+ * [pg8000.dbapi.Connection.close()](#pg8000dbapiconnectionclose)
+ * [pg8000.dbapi.Connection.cursor()](#pg8000dbapiconnectioncursor)
+ * [pg8000.dbapi.Connection.rollback()](#pg8000dbapiconnectionrollback)
+ * [pg8000.dbapi.Connection.tpc\_begin(xid)](#pg8000dbapiconnectiontpc_beginxid)
+ * [pg8000.dbapi.Connection.tpc_commit(xid=None)](#pg8000dbapiconnectiontpc_commitxidnone)
+ * [pg8000.dbapi.Connection.tpc_prepare()](#pg8000dbapiconnectiontpc_prepare)
+ * [pg8000.dbapi.Connection.tpc_recover()](#pg8000dbapiconnectiontpc_recover)
+ * [pg8000.dbapi.Connection.tpc_rollback(xid=None)](#pg8000dbapiconnectiontpc_rollbackxidnone)
+ * [pg8000.dbapi.Connection.xid(format_id, global_transaction_id, branch_qualifier)](#pg8000dbapiconnectionxidformat_id-global_transaction_id-branch_qualifier)
+ * [pg8000.dbapi.Cursor](#pg8000dbapicursor)
+ * [pg8000.dbapi.Cursor.arraysize](#pg8000dbapicursorarraysize)
+ * [pg8000.dbapi.Cursor.connection](#pg8000dbapicursorconnection)
+ * [pg8000.dbapi.Cursor.rowcount](#pg8000dbapicursorrowcount)
+ * [pg8000.dbapi.Cursor.description](#pg8000dbapicursordescription)
+ * [pg8000.dbapi.Cursor.close()](#pg8000dbapicursorclose)
+ * [pg8000.dbapi.Cursor.execute(operation, args=None, stream=None)](#pg8000dbapicursorexecuteoperation-argsnone-streamnone)
+ * [pg8000.dbapi.Cursor.executemany(operation, param_sets)](#pg8000dbapicursorexecutemanyoperation-param_sets)
+ * [pg8000.dbapi.Cursor.callproc(procname, parameters=None)](#pg8000dbapicursorcallprocprocname-parametersnone)
+ * [pg8000.dbapi.Cursor.fetchall()](#pg8000dbapicursorfetchall)
+ * [pg8000.dbapi.Cursor.fetchmany(size=None)](#pg8000dbapicursorfetchmanysizenone)
+ * [pg8000.dbapi.Cursor.fetchone()](#pg8000dbapicursorfetchone)
+ * [pg8000.dbapi.Cursor.setinputsizes(\*sizes)](#pg8000dbapicursorsetinputsizessizes)
+ * [pg8000.dbapi.Cursor.setoutputsize(size, column=None)](#pg8000dbapicursorsetoutputsizesize-columnnone)
+ * [pg8000.dbapi.Interval](#pg8000dbapiinterval)
+* [Design Decisions](#design-decisions)
+* [Tests](#tests)
+* [Doing A Release Of pg8000](#doing-a-release-of-pg8000)
+* [Release Notes](#release-notes)
+ * [Version 1.31.5, 2025-09-14](#version-1315-2025-09-14)
+ * [Version 1.31.4, 2025-07-20](#version-1314-2025-07-20)
+ * [Version 1.31.3, 2025-07-19](#version-1313-2025-07-19)
+ * [Version 1.31.2, 2024-04-28](#version-1312-2024-04-28)
+ * [Version 1.31.1, 2024-04-01](#version-1311-2024-04-01)
+ * [Version 1.31.0, 2024-03-31](#version-1310-2024-03-31)
+ * [Version 1.30.5, 2024-02-22](#version-1305-2024-02-22)
+ * [Version 1.30.4, 2024-01-03](#version-1304-2024-01-03)
+ * [Version 1.30.3, 2023-10-31](#version-1303-2023-10-31)
+ * [Version 1.30.2, 2023-09-17](#version-1302-2023-09-17)
+ * [Version 1.30.1, 2023-07-29](#version-1301-2023-07-29)
+ * [Version 1.30.0, 2023-07-27](#version-1300-2023-07-27)
+ * [Version 1.29.8, 2023-06-16](#version-1298-2023-06-16)
+ * [Version 1.29.7, 2023-06-16](#version-1297-2023-06-16)
+ * [Version 1.29.6, 2023-05-29](#version-1296-2023-05-29)
+ * [Version 1.29.5, 2023-05-09](#version-1295-2023-05-09)
+ * [Version 1.29.4, 2022-12-14](#version-1294-2022-12-14)
+ * [Version 1.29.3, 2022-10-26](#version-1293-2022-10-26)
+ * [Version 1.29.2, 2022-10-09](#version-1292-2022-10-09)
+ * [Version 1.29.1, 2022-05-23](#version-1291-2022-05-23)
+ * [Version 1.29.0, 2022-05-21](#version-1290-2022-05-21)
+ * [Version 1.28.3, 2022-05-18](#version-1283-2022-05-18)
+ * [Version 1.28.2, 2022-05-17](#version-1282-2022-05-17)
+ * [Version 1.28.1, 2022-05-17](#version-1281-2022-05-17)
+ * [Version 1.28.0, 2022-05-17](#version-1280-2022-05-17)
+ * [Version 1.27.1, 2022-05-16](#version-1271-2022-05-16)
+ * [Version 1.27.0, 2022-05-16](#version-1270-2022-05-16)
+ * [Version 1.26.1, 2022-04-23](#version-1261-2022-04-23)
+ * [Version 1.26.0, 2022-04-18](#version-1260-2022-04-18)
+ * [Version 1.25.0, 2022-04-17](#version-1250-2022-04-17)
+ * [Version 1.24.2, 2022-04-15](#version-1242-2022-04-15)
+ * [Version 1.24.1, 2022-03-02](#version-1241-2022-03-02)
+ * [Version 1.24.0, 2022-02-06](#version-1240-2022-02-06)
+ * [Version 1.23.0, 2021-11-13](#version-1230-2021-11-13)
+ * [Version 1.22.1, 2021-11-10](#version-1221-2021-11-10)
+ * [Version 1.22.0, 2021-10-13](#version-1220-2021-10-13)
+
+<!-- mtoc-end -->
## Installation
@@ -140,7 +302,7 @@ rolling back a transaction:
```
-NB. There is [a longstanding bug](https://github.com/tlocke/pg8000/issues/36>) in the
+NB. There is [a longstanding bug](https://codeberg.org/tlocke/pg8000/issues/36) in the
PostgreSQL server whereby if a `COMMIT` is issued against a failed transaction, the
transaction is silently rolled back, rather than an error being returned. pg8000
attempts to detect when this has happened and raise an `InterfaceError`.
@@ -2137,6 +2299,13 @@ twine upload dist/*
## Release Notes
+### Version 1.31.5, 2025-09-14
+
+- Tiny performance improvement in reading from socket.
+- Fix bug in `literal()` where list is not properly escaped.
+- Move to [Codeberg](https://codeberg.org/tlocke/pg8000).
+
+
### Version 1.31.4, 2025-07-20
- Various speed optimisations.
diff --git a/contrib/python/pg8000/README.md b/contrib/python/pg8000/README.md
index d351b68c4ea..28abf15cb65 100644
--- a/contrib/python/pg8000/README.md
+++ b/contrib/python/pg8000/README.md
@@ -6,10 +6,172 @@ pg8000 is a pure-[Python](https://www.python.org/)
belief that it is probably about the 8000th PostgreSQL interface for Python. pg8000 is
distributed under the BSD 3-clause license.
-All bug reports, feature requests and contributions are welcome at
-[http://github.com/tlocke/pg8000/](http://github.com/tlocke/pg8000/).
+<!-- mtoc-start -->
-[![Workflow Status Badge](https://github.com/tlocke/pg8000/actions/workflows/test.yml/badge.svg)](https://github.com/tlocke/pg8000/actions/workflows/test.yml)
+* [Installation](#installation)
+* [Native API Interactive Examples](#native-api-interactive-examples)
+ * [Basic Example](#basic-example)
+ * [Transactions](#transactions)
+ * [Query Using Functions](#query-using-functions)
+ * [Interval Type](#interval-type)
+ * [Point Type](#point-type)
+ * [Client Encoding](#client-encoding)
+ * [JSON](#json)
+ * [Retrieve Column Metadata From Results](#retrieve-column-metadata-from-results)
+ * [Notices And Notifications](#notices-and-notifications)
+ * [Parameter Statuses](#parameter-statuses)
+ * [LIMIT ALL](#limit-all)
+ * [IN and NOT IN](#in-and-not-in)
+ * [Many SQL Statements Can't Be Parameterized](#many-sql-statements-cant-be-parameterized)
+ * [COPY FROM And TO A Stream](#copy-from-and-to-a-stream)
+ * [Execute Multiple SQL Statements](#execute-multiple-sql-statements)
+ * [Quoted Identifiers in SQL](#quoted-identifiers-in-sql)
+ * [Custom adapter from a Python type to a PostgreSQL type](#custom-adapter-from-a-python-type-to-a-postgresql-type)
+ * [Custom adapter from a PostgreSQL type to a Python type](#custom-adapter-from-a-postgresql-type-to-a-python-type)
+ * [Could Not Determine Data Type Of Parameter](#could-not-determine-data-type-of-parameter)
+ * [Prepared Statements](#prepared-statements)
+ * [Use Environment Variables As Connection Defaults](#use-environment-variables-as-connection-defaults)
+ * [Connect To PostgreSQL Over SSL](#connect-to-postgresql-over-ssl)
+ * [Server-Side Cursors](#server-side-cursors)
+ * [BLOBs (Binary Large Objects)](#blobs-binary-large-objects)
+ * [Replication Protocol](#replication-protocol)
+ * [Extra Startup Parameters](#extra-startup-parameters)
+ * [PostgreSQL-Style Parameter Placeholders](#postgresql-style-parameter-placeholders)
+* [DB-API 2 Interactive Examples](#db-api-2-interactive-examples)
+ * [Basic Example](#basic-example-1)
+ * [Query Using Functions](#query-using-functions-1)
+ * [Interval Type](#interval-type-1)
+ * [Point Type](#point-type-1)
+ * [Numeric Parameter Style](#numeric-parameter-style)
+ * [Autocommit](#autocommit)
+ * [Client Encoding](#client-encoding-1)
+ * [JSON](#json-1)
+ * [Retrieve Column Names From Results](#retrieve-column-names-from-results)
+ * [COPY from and to a file](#copy-from-and-to-a-file)
+ * [Server-Side Cursors](#server-side-cursors-1)
+ * [BLOBs (Binary Large Objects)](#blobs-binary-large-objects-1)
+ * [Parameter Limit](#parameter-limit)
+* [Type Mapping](#type-mapping)
+* [Theory Of Operation](#theory-of-operation)
+* [Native API Docs](#native-api-docs)
+ * [pg8000.native.Error](#pg8000nativeerror)
+ * [pg8000.native.InterfaceError](#pg8000nativeinterfaceerror)
+ * [pg8000.native.DatabaseError](#pg8000nativedatabaseerror)
+ * [pg8000.native.Connection(user, host='localhost', database=None, port=5432, password=None, source\_address=None, unix\_sock=None, ssl\_context=None, timeout=None, tcp\_keepalive=True, application\_name=None, replication=None, sock=None)](#pg8000nativeconnectionuser-hostlocalhost-databasenone-port5432-passwordnone-source_addressnone-unix_socknone-ssl_contextnone-timeoutnone-tcp_keepalivetrue-application_namenone-replicationnone-socknone)
+ * [pg8000.native.Connection.notifications](#pg8000nativeconnectionnotifications)
+ * [pg8000.native.Connection.notices](#pg8000nativeconnectionnotices)
+ * [pg8000.native.Connection.parameter\_statuses](#pg8000nativeconnectionparameter_statuses)
+ * [pg8000.native.Connection.run(sql, stream=None, types=None, \*\*kwargs)](#pg8000nativeconnectionrunsql-streamnone-typesnone-kwargs)
+ * [pg8000.native.Connection.row\_count](#pg8000nativeconnectionrow_count)
+ * [pg8000.native.Connection.columns](#pg8000nativeconnectioncolumns)
+ * [pg8000.native.Connection.close()](#pg8000nativeconnectionclose)
+ * [pg8000.native.Connection.register\_out\_adapter(typ, out\_func)](#pg8000nativeconnectionregister_out_adaptertyp-out_func)
+ * [pg8000.native.Connection.register\_in\_adapter(oid, in\_func)](#pg8000nativeconnectionregister_in_adapteroid-in_func)
+ * [pg8000.native.Connection.prepare(sql)](#pg8000nativeconnectionpreparesql)
+ * [pg8000.native.PreparedStatement](#pg8000nativepreparedstatement)
+ * [pg8000.native.PreparedStatement.run(\*\*kwargs)](#pg8000nativepreparedstatementrunkwargs)
+ * [pg8000.native.PreparedStatement.close()](#pg8000nativepreparedstatementclose)
+ * [pg8000.native.identifier(ident)](#pg8000nativeidentifierident)
+ * [pg8000.native.literal(value)](#pg8000nativeliteralvalue)
+* [DB-API 2 Docs](#db-api-2-docs)
+ * [Properties](#properties)
+ * [pg8000.dbapi.apilevel](#pg8000dbapiapilevel)
+ * [pg8000.dbapi.threadsafety](#pg8000dbapithreadsafety)
+ * [pg8000.dbapi.paramstyle](#pg8000dbapiparamstyle)
+ * [pg8000.dbapi.STRING](#pg8000dbapistring)
+ * [pg8000.dbapi.BINARY](#pg8000dbapibinary)
+ * [pg8000.dbapi.NUMBER](#pg8000dbapinumber)
+ * [pg8000.dbapi.DATETIME](#pg8000dbapidatetime)
+ * [pg8000.dbapi.ROWID](#pg8000dbapirowid)
+ * [Functions](#functions)
+ * [pg8000.dbapi.connect(user, host='localhost', database=None, port=5432, password=None, source\_address=None, unix\_sock=None, ssl\_context=None, timeout=None, tcp\_keepalive=True, applicationa_name=None, replication=None, sock=None)](#pg8000dbapiconnectuser-hostlocalhost-databasenone-port5432-passwordnone-source_addressnone-unix_socknone-ssl_contextnone-timeoutnone-tcp_keepalivetrue-applicationa_namenone-replicationnone-socknone)
+ * [pg8000.dbapi.Date(year, month, day)](#pg8000dbapidateyear-month-day)
+ * [pg8000.dbapi.Time(hour, minute, second)](#pg8000dbapitimehour-minute-second)
+ * [pg8000.dbapi.Timestamp(year, month, day, hour, minute, second)](#pg8000dbapitimestampyear-month-day-hour-minute-second)
+ * [pg8000.dbapi.DateFromTicks(ticks)](#pg8000dbapidatefromticksticks)
+ * [pg8000.dbapi.TimeFromTicks(ticks)](#pg8000dbapitimefromticksticks)
+ * [pg8000.dbapi.TimestampFromTicks(ticks)](#pg8000dbapitimestampfromticksticks)
+ * [pg8000.dbapi.Binary(value)](#pg8000dbapibinaryvalue)
+ * [Generic Exceptions](#generic-exceptions)
+ * [pg8000.dbapi.Warning](#pg8000dbapiwarning)
+ * [pg8000.dbapi.Error](#pg8000dbapierror)
+ * [pg8000.dbapi.InterfaceError](#pg8000dbapiinterfaceerror)
+ * [pg8000.dbapi.DatabaseError](#pg8000dbapidatabaseerror)
+ * [pg8000.dbapi.DataError](#pg8000dbapidataerror)
+ * [pg8000.dbapi.OperationalError](#pg8000dbapioperationalerror)
+ * [pg8000.dbapi.IntegrityError](#pg8000dbapiintegrityerror)
+ * [pg8000.dbapi.InternalError](#pg8000dbapiinternalerror)
+ * [pg8000.dbapi.ProgrammingError](#pg8000dbapiprogrammingerror)
+ * [pg8000.dbapi.NotSupportedError](#pg8000dbapinotsupportederror)
+ * [Classes](#classes)
+ * [pg8000.dbapi.Connection](#pg8000dbapiconnection)
+ * [pg8000.dbapi.Connection.autocommit](#pg8000dbapiconnectionautocommit)
+ * [pg8000.dbapi.Connection.close()](#pg8000dbapiconnectionclose)
+ * [pg8000.dbapi.Connection.cursor()](#pg8000dbapiconnectioncursor)
+ * [pg8000.dbapi.Connection.rollback()](#pg8000dbapiconnectionrollback)
+ * [pg8000.dbapi.Connection.tpc\_begin(xid)](#pg8000dbapiconnectiontpc_beginxid)
+ * [pg8000.dbapi.Connection.tpc_commit(xid=None)](#pg8000dbapiconnectiontpc_commitxidnone)
+ * [pg8000.dbapi.Connection.tpc_prepare()](#pg8000dbapiconnectiontpc_prepare)
+ * [pg8000.dbapi.Connection.tpc_recover()](#pg8000dbapiconnectiontpc_recover)
+ * [pg8000.dbapi.Connection.tpc_rollback(xid=None)](#pg8000dbapiconnectiontpc_rollbackxidnone)
+ * [pg8000.dbapi.Connection.xid(format_id, global_transaction_id, branch_qualifier)](#pg8000dbapiconnectionxidformat_id-global_transaction_id-branch_qualifier)
+ * [pg8000.dbapi.Cursor](#pg8000dbapicursor)
+ * [pg8000.dbapi.Cursor.arraysize](#pg8000dbapicursorarraysize)
+ * [pg8000.dbapi.Cursor.connection](#pg8000dbapicursorconnection)
+ * [pg8000.dbapi.Cursor.rowcount](#pg8000dbapicursorrowcount)
+ * [pg8000.dbapi.Cursor.description](#pg8000dbapicursordescription)
+ * [pg8000.dbapi.Cursor.close()](#pg8000dbapicursorclose)
+ * [pg8000.dbapi.Cursor.execute(operation, args=None, stream=None)](#pg8000dbapicursorexecuteoperation-argsnone-streamnone)
+ * [pg8000.dbapi.Cursor.executemany(operation, param_sets)](#pg8000dbapicursorexecutemanyoperation-param_sets)
+ * [pg8000.dbapi.Cursor.callproc(procname, parameters=None)](#pg8000dbapicursorcallprocprocname-parametersnone)
+ * [pg8000.dbapi.Cursor.fetchall()](#pg8000dbapicursorfetchall)
+ * [pg8000.dbapi.Cursor.fetchmany(size=None)](#pg8000dbapicursorfetchmanysizenone)
+ * [pg8000.dbapi.Cursor.fetchone()](#pg8000dbapicursorfetchone)
+ * [pg8000.dbapi.Cursor.setinputsizes(\*sizes)](#pg8000dbapicursorsetinputsizessizes)
+ * [pg8000.dbapi.Cursor.setoutputsize(size, column=None)](#pg8000dbapicursorsetoutputsizesize-columnnone)
+ * [pg8000.dbapi.Interval](#pg8000dbapiinterval)
+* [Design Decisions](#design-decisions)
+* [Tests](#tests)
+* [Doing A Release Of pg8000](#doing-a-release-of-pg8000)
+* [Release Notes](#release-notes)
+ * [Version 1.31.5, 2025-09-14](#version-1315-2025-09-14)
+ * [Version 1.31.4, 2025-07-20](#version-1314-2025-07-20)
+ * [Version 1.31.3, 2025-07-19](#version-1313-2025-07-19)
+ * [Version 1.31.2, 2024-04-28](#version-1312-2024-04-28)
+ * [Version 1.31.1, 2024-04-01](#version-1311-2024-04-01)
+ * [Version 1.31.0, 2024-03-31](#version-1310-2024-03-31)
+ * [Version 1.30.5, 2024-02-22](#version-1305-2024-02-22)
+ * [Version 1.30.4, 2024-01-03](#version-1304-2024-01-03)
+ * [Version 1.30.3, 2023-10-31](#version-1303-2023-10-31)
+ * [Version 1.30.2, 2023-09-17](#version-1302-2023-09-17)
+ * [Version 1.30.1, 2023-07-29](#version-1301-2023-07-29)
+ * [Version 1.30.0, 2023-07-27](#version-1300-2023-07-27)
+ * [Version 1.29.8, 2023-06-16](#version-1298-2023-06-16)
+ * [Version 1.29.7, 2023-06-16](#version-1297-2023-06-16)
+ * [Version 1.29.6, 2023-05-29](#version-1296-2023-05-29)
+ * [Version 1.29.5, 2023-05-09](#version-1295-2023-05-09)
+ * [Version 1.29.4, 2022-12-14](#version-1294-2022-12-14)
+ * [Version 1.29.3, 2022-10-26](#version-1293-2022-10-26)
+ * [Version 1.29.2, 2022-10-09](#version-1292-2022-10-09)
+ * [Version 1.29.1, 2022-05-23](#version-1291-2022-05-23)
+ * [Version 1.29.0, 2022-05-21](#version-1290-2022-05-21)
+ * [Version 1.28.3, 2022-05-18](#version-1283-2022-05-18)
+ * [Version 1.28.2, 2022-05-17](#version-1282-2022-05-17)
+ * [Version 1.28.1, 2022-05-17](#version-1281-2022-05-17)
+ * [Version 1.28.0, 2022-05-17](#version-1280-2022-05-17)
+ * [Version 1.27.1, 2022-05-16](#version-1271-2022-05-16)
+ * [Version 1.27.0, 2022-05-16](#version-1270-2022-05-16)
+ * [Version 1.26.1, 2022-04-23](#version-1261-2022-04-23)
+ * [Version 1.26.0, 2022-04-18](#version-1260-2022-04-18)
+ * [Version 1.25.0, 2022-04-17](#version-1250-2022-04-17)
+ * [Version 1.24.2, 2022-04-15](#version-1242-2022-04-15)
+ * [Version 1.24.1, 2022-03-02](#version-1241-2022-03-02)
+ * [Version 1.24.0, 2022-02-06](#version-1240-2022-02-06)
+ * [Version 1.23.0, 2021-11-13](#version-1230-2021-11-13)
+ * [Version 1.22.1, 2021-11-10](#version-1221-2021-11-10)
+ * [Version 1.22.0, 2021-10-13](#version-1220-2021-10-13)
+
+<!-- mtoc-end -->
## Installation
@@ -110,7 +272,7 @@ rolling back a transaction:
```
-NB. There is [a longstanding bug](https://github.com/tlocke/pg8000/issues/36>) in the
+NB. There is [a longstanding bug](https://codeberg.org/tlocke/pg8000/issues/36) in the
PostgreSQL server whereby if a `COMMIT` is issued against a failed transaction, the
transaction is silently rolled back, rather than an error being returned. pg8000
attempts to detect when this has happened and raise an `InterfaceError`.
@@ -2107,6 +2269,13 @@ twine upload dist/*
## Release Notes
+### Version 1.31.5, 2025-09-14
+
+- Tiny performance improvement in reading from socket.
+- Fix bug in `literal()` where list is not properly escaped.
+- Move to [Codeberg](https://codeberg.org/tlocke/pg8000).
+
+
### Version 1.31.4, 2025-07-20
- Various speed optimisations.
diff --git a/contrib/python/pg8000/pg8000/converters.py b/contrib/python/pg8000/pg8000/converters.py
index 9a3c0937599..fe025a53ac5 100644
--- a/contrib/python/pg8000/pg8000/converters.py
+++ b/contrib/python/pg8000/pg8000/converters.py
@@ -833,4 +833,4 @@ def _(value: Timedelta):
@literal.register
def _(value: list):
- return f"'{array_out(value)}'"
+ return f"{literal(array_out(value))}"
diff --git a/contrib/python/pg8000/pg8000/core.py b/contrib/python/pg8000/pg8000/core.py
index f4f59f22434..b4524806f2e 100644
--- a/contrib/python/pg8000/pg8000/core.py
+++ b/contrib/python/pg8000/pg8000/core.py
@@ -147,7 +147,7 @@ def _flush(sock):
def _read(sock, size):
- buff = bytearray()
+ buff = bytearray(sock.read(size))
try:
while len(buff) < size:
block = sock.read(size - len(buff))
diff --git a/contrib/python/pg8000/ya.make b/contrib/python/pg8000/ya.make
index 949b64fa399..78adf678d54 100644
--- a/contrib/python/pg8000/ya.make
+++ b/contrib/python/pg8000/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(1.31.4)
+VERSION(1.31.5)
LICENSE(BSD-3-Clause)
diff --git a/contrib/python/pyparsing/py3/.dist-info/METADATA b/contrib/python/pyparsing/py3/.dist-info/METADATA
index d03671b6130..0c6093fe53e 100644
--- a/contrib/python/pyparsing/py3/.dist-info/METADATA
+++ b/contrib/python/pyparsing/py3/.dist-info/METADATA
@@ -1,14 +1,14 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: pyparsing
-Version: 3.2.3
-Summary: pyparsing module - Classes and methods to define and execute parsing grammars
+Version: 3.2.5
+Summary: pyparsing - Classes and methods to define and execute parsing grammars
Author-email: Paul McGuire <[email protected]>
Requires-Python: >=3.9
Description-Content-Type: text/x-rst
+License-Expression: MIT
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
-Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
@@ -24,6 +24,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Compilers
Classifier: Topic :: Text Processing
Classifier: Typing :: Typed
+License-File: LICENSE
Requires-Dist: railroad-diagrams ; extra == "diagrams"
Requires-Dist: jinja2 ; extra == "diagrams"
Project-URL: Homepage, https://github.com/pyparsing/pyparsing/
diff --git a/contrib/python/pyparsing/py3/pyparsing/__init__.py b/contrib/python/pyparsing/py3/pyparsing/__init__.py
index d839fd25375..502519cdef3 100644
--- a/contrib/python/pyparsing/py3/pyparsing/__init__.py
+++ b/contrib/python/pyparsing/py3/pyparsing/__init__.py
@@ -23,21 +23,23 @@
#
__doc__ = """
-pyparsing module - Classes and methods to define and execute parsing grammars
-=============================================================================
+pyparsing - Classes and methods to define and execute parsing grammars
+======================================================================
-The pyparsing module is an alternative approach to creating and
-executing simple grammars, vs. the traditional lex/yacc approach, or the
-use of regular expressions. With pyparsing, you don't need to learn
-a new syntax for defining grammars or matching expressions - the parsing
-module provides a library of classes that you use to construct the
-grammar directly in Python.
+Pyparsing is an alternative approach to creating and executing simple
+grammars, vs. the traditional lex/yacc approach, or the use of regular
+expressions. With pyparsing, you don't need to learn a new syntax for
+defining grammars or matching expressions - the parsing module provides
+a library of classes that you use to construct the grammar directly in
+Python.
Here is a program to parse "Hello, World!" (or any greeting of the form
``"<salutation>, <addressee>!"``), built up using :class:`Word`,
:class:`Literal`, and :class:`And` elements
(the :meth:`'+'<ParserElement.__add__>` operators create :class:`And` expressions,
-and the strings are auto-converted to :class:`Literal` expressions)::
+and the strings are auto-converted to :class:`Literal` expressions):
+
+.. testcode::
from pyparsing import Word, alphas
@@ -47,7 +49,9 @@ and the strings are auto-converted to :class:`Literal` expressions)::
hello = "Hello, World!"
print(hello, "->", greet.parse_string(hello))
-The program outputs the following::
+The program outputs the following:
+
+.. testoutput::
Hello, World! -> ['Hello', ',', 'World', '!']
@@ -69,8 +73,8 @@ vexing when writing text parsers:
- embedded comments
-Getting Started -
------------------
+Getting Started
+---------------
Visit the classes :class:`ParserElement` and :class:`ParseResults` to
see the base classes that most other pyparsing
classes inherit from. Use the docstrings for examples of how to:
@@ -120,8 +124,8 @@ class version_info(NamedTuple):
return f"{__name__}.{type(self).__name__}({', '.join('{}={!r}'.format(*nv) for nv in zip(self._fields, self))})"
-__version_info__ = version_info(3, 2, 3, "final", 1)
-__version_time__ = "25 Mar 2025 01:38 UTC"
+__version_info__ = version_info(3, 2, 5, "final", 1)
+__version_time__ = "16 Sep 2025 22:24 UTC"
__version__ = __version_info__.__version__
__versionTime__ = __version_time__
__author__ = "Paul McGuire <[email protected]>"
diff --git a/contrib/python/pyparsing/py3/pyparsing/actions.py b/contrib/python/pyparsing/py3/pyparsing/actions.py
index 0153cc7132a..0d80d2cf911 100644
--- a/contrib/python/pyparsing/py3/pyparsing/actions.py
+++ b/contrib/python/pyparsing/py3/pyparsing/actions.py
@@ -56,36 +56,45 @@ def match_only_at_col(n: int) -> ParseAction:
return verify_col
-def replace_with(repl_str: str) -> ParseAction:
+def replace_with(repl_str: Any) -> ParseAction:
"""
Helper method for common parse actions that simply return
a literal value. Especially useful when used with
- :class:`transform_string<ParserElement.transform_string>` ().
+ :meth:`~ParserElement.transform_string`.
- Example::
+ Example:
- num = Word(nums).set_parse_action(lambda toks: int(toks[0]))
- na = one_of("N/A NA").set_parse_action(replace_with(math.nan))
- term = na | num
+ .. doctest::
- term[1, ...].parse_string("324 234 N/A 234") # -> [324, 234, nan, 234]
+ >>> num = Word(nums).set_parse_action(lambda toks: int(toks[0]))
+ >>> na = one_of("N/A NA").set_parse_action(replace_with(math.nan))
+ >>> term = na | num
+
+ >>> term[1, ...].parse_string("324 234 N/A 234")
+ ParseResults([324, 234, nan, 234], {})
"""
return lambda s, l, t: [repl_str]
def remove_quotes(s: str, l: int, t: ParseResults) -> Any:
- """
+ r"""
Helper parse action for removing quotation marks from parsed
- quoted strings.
+ quoted strings, that use a single character for quoting. For parsing
+ strings that may have multiple characters, use the :class:`QuotedString`
+ class.
+
+ Example:
- Example::
+ .. doctest::
- # by default, quotation marks are included in parsed results
- quoted_string.parse_string("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
+ >>> # by default, quotation marks are included in parsed results
+ >>> quoted_string.parse_string("'Now is the Winter of our Discontent'")
+ ParseResults(["'Now is the Winter of our Discontent'"], {})
- # use remove_quotes to strip quotation marks from parsed results
- quoted_string.set_parse_action(remove_quotes)
- quoted_string.parse_string("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
+ >>> # use remove_quotes to strip quotation marks from parsed results
+ >>> dequoted = quoted_string().set_parse_action(remove_quotes)
+ >>> dequoted.parse_string("'Now is the Winter of our Discontent'")
+ ParseResults(['Now is the Winter of our Discontent'], {})
"""
return t[0][1:-1]
@@ -115,36 +124,53 @@ def with_attribute(*args: tuple[str, str], **attr_dict) -> ParseAction:
To verify that the attribute exists, but without specifying a value,
pass ``with_attribute.ANY_VALUE`` as the value.
- Example::
+ The next two examples use the following input data and tag parsers:
+
+ .. testcode::
+
+ html = '''
+ <div>
+ Some text
+ <div type="grid">1 4 0 1 0</div>
+ <div type="graph">1,3 2,3 1,1</div>
+ <div>this has no type</div>
+ </div>
+ '''
+ div,div_end = make_html_tags("div")
+
+ Only match div tag having a type attribute with value "grid":
+
+ .. testcode::
+
+ div_grid = div().set_parse_action(with_attribute(type="grid"))
+ grid_expr = div_grid + SkipTo(div | div_end)("body")
+ for grid_header in grid_expr.search_string(html):
+ print(grid_header.body)
+
+ prints:
- html = '''
- <div>
- Some text
- <div type="grid">1 4 0 1 0</div>
- <div type="graph">1,3 2,3 1,1</div>
- <div>this has no type</div>
- </div>
- '''
- div,div_end = make_html_tags("div")
+ .. testoutput::
- # only match div tag having a type attribute with value "grid"
- div_grid = div().set_parse_action(with_attribute(type="grid"))
- grid_expr = div_grid + SkipTo(div | div_end)("body")
- for grid_header in grid_expr.search_string(html):
- print(grid_header.body)
+ 1 4 0 1 0
- # construct a match with any div tag having a type attribute, regardless of the value
- div_any_type = div().set_parse_action(with_attribute(type=with_attribute.ANY_VALUE))
- div_expr = div_any_type + SkipTo(div | div_end)("body")
- for div_header in div_expr.search_string(html):
- print(div_header.body)
+ Construct a match with any div tag having a type attribute,
+ regardless of the value:
- prints::
+ .. testcode::
- 1 4 0 1 0
+ div_any_type = div().set_parse_action(
+ with_attribute(type=with_attribute.ANY_VALUE)
+ )
+ div_expr = div_any_type + SkipTo(div | div_end)("body")
+ for div_header in div_expr.search_string(html):
+ print(div_header.body)
- 1 4 0 1 0
- 1,3 2,3 1,1
+ prints:
+
+ .. testoutput::
+
+ 1 4 0 1 0
+ 1,3 2,3 1,1
"""
attrs_list: list[tuple[str, str]] = []
if args:
@@ -171,39 +197,57 @@ with_attribute.ANY_VALUE = object() # type: ignore [attr-defined]
def with_class(classname: str, namespace: str = "") -> ParseAction:
"""
- Simplified version of :class:`with_attribute` when
+ Simplified version of :meth:`with_attribute` when
matching on a div class - made difficult because ``class`` is
a reserved word in Python.
- Example::
+ Using similar input data to the :meth:`with_attribute` examples:
+
+ .. testcode::
+
+ html = '''
+ <div>
+ Some text
+ <div class="grid">1 4 0 1 0</div>
+ <div class="graph">1,3 2,3 1,1</div>
+ <div>this &lt;div&gt; has no class</div>
+ </div>
+ '''
+ div,div_end = make_html_tags("div")
+
+ Only match div tag having the "grid" class:
+
+ .. testcode::
+
+ div_grid = div().set_parse_action(with_class("grid"))
+ grid_expr = div_grid + SkipTo(div | div_end)("body")
+ for grid_header in grid_expr.search_string(html):
+ print(grid_header.body)
+
+ prints:
+
+ .. testoutput::
- html = '''
- <div>
- Some text
- <div class="grid">1 4 0 1 0</div>
- <div class="graph">1,3 2,3 1,1</div>
- <div>this &lt;div&gt; has no class</div>
- </div>
+ 1 4 0 1 0
- '''
- div,div_end = make_html_tags("div")
- div_grid = div().set_parse_action(with_class("grid"))
+ Construct a match with any div tag having a class attribute,
+ regardless of the value:
- grid_expr = div_grid + SkipTo(div | div_end)("body")
- for grid_header in grid_expr.search_string(html):
- print(grid_header.body)
+ .. testcode::
- div_any_type = div().set_parse_action(with_class(withAttribute.ANY_VALUE))
- div_expr = div_any_type + SkipTo(div | div_end)("body")
- for div_header in div_expr.search_string(html):
- print(div_header.body)
+ div_any_type = div().set_parse_action(
+ with_class(withAttribute.ANY_VALUE)
+ )
+ div_expr = div_any_type + SkipTo(div | div_end)("body")
+ for div_header in div_expr.search_string(html):
+ print(div_header.body)
- prints::
+ prints:
- 1 4 0 1 0
+ .. testoutput::
- 1 4 0 1 0
- 1,3 2,3 1,1
+ 1 4 0 1 0
+ 1,3 2,3 1,1
"""
classattr = f"{namespace}:class" if namespace else "class"
return with_attribute(**{classattr: classname})
diff --git a/contrib/python/pyparsing/py3/pyparsing/common.py b/contrib/python/pyparsing/py3/pyparsing/common.py
index e46511086da..dbf9ba88e9d 100644
--- a/contrib/python/pyparsing/py3/pyparsing/common.py
+++ b/contrib/python/pyparsing/py3/pyparsing/common.py
@@ -30,7 +30,9 @@ class pyparsing_common:
- :class:`upcase_tokens`
- :class:`downcase_tokens`
- Example::
+ Examples:
+
+ .. testcode::
pyparsing_common.number.run_tests('''
# any int or real number, returned as the appropriate type
@@ -42,44 +44,9 @@ class pyparsing_common:
1e-12
''')
- pyparsing_common.fnumber.run_tests('''
- # any int or real number, returned as float
- 100
- -100
- +100
- 3.14159
- 6.02e23
- 1e-12
- ''')
-
- pyparsing_common.hex_integer.run_tests('''
- # hex numbers
- 100
- FF
- ''')
-
- pyparsing_common.fraction.run_tests('''
- # fractions
- 1/2
- -3/4
- ''')
-
- pyparsing_common.mixed_integer.run_tests('''
- # mixed fractions
- 1
- 1/2
- -3/4
- 1-3/4
- ''')
-
- import uuid
- pyparsing_common.uuid.set_parse_action(token_map(uuid.UUID))
- pyparsing_common.uuid.run_tests('''
- # uuid
- 12345678-1234-5678-1234-567812345678
- ''')
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
- prints::
# any int or real number, returned as the appropriate type
100
@@ -100,6 +67,22 @@ class pyparsing_common:
1e-12
[1e-12]
+ .. testcode::
+
+ pyparsing_common.fnumber.run_tests('''
+ # any int or real number, returned as float
+ 100
+ -100
+ +100
+ 3.14159
+ 6.02e23
+ 1e-12
+ ''')
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+
+
# any int or real number, returned as float
100
[100.0]
@@ -119,6 +102,18 @@ class pyparsing_common:
1e-12
[1e-12]
+ .. testcode::
+
+ pyparsing_common.hex_integer.run_tests('''
+ # hex numbers
+ 100
+ FF
+ ''')
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+
+
# hex numbers
100
[256]
@@ -126,6 +121,18 @@ class pyparsing_common:
FF
[255]
+ .. testcode::
+
+ pyparsing_common.fraction.run_tests('''
+ # fractions
+ 1/2
+ -3/4
+ ''')
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+
+
# fractions
1/2
[0.5]
@@ -133,6 +140,20 @@ class pyparsing_common:
-3/4
[-0.75]
+ .. testcode::
+
+ pyparsing_common.mixed_integer.run_tests('''
+ # mixed fractions
+ 1
+ 1/2
+ -3/4
+ 1-3/4
+ ''')
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+
+
# mixed fractions
1
[1]
@@ -145,6 +166,18 @@ class pyparsing_common:
1-3/4
[1.75]
+ .. testcode::
+
+ import uuid
+ pyparsing_common.uuid.set_parse_action(token_map(uuid.UUID))
+ pyparsing_common.uuid.run_tests('''
+ # uuid
+ 12345678-1234-5678-1234-567812345678
+ ''')
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+
# uuid
12345678-1234-5678-1234-567812345678
@@ -264,13 +297,17 @@ class pyparsing_common:
Params -
- fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%d"``)
- Example::
+ Example:
+
+ .. testcode::
date_expr = pyparsing_common.iso8601_date.copy()
date_expr.set_parse_action(pyparsing_common.convert_to_date())
print(date_expr.parse_string("1999-12-31"))
- prints::
+ prints:
+
+ .. testoutput::
[datetime.date(1999, 12, 31)]
"""
@@ -291,13 +328,17 @@ class pyparsing_common:
Params -
- fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%dT%H:%M:%S.%f"``)
- Example::
+ Example:
+
+ .. testcode::
dt_expr = pyparsing_common.iso8601_datetime.copy()
dt_expr.set_parse_action(pyparsing_common.convert_to_datetime())
print(dt_expr.parse_string("1999-12-31T23:59:59.999"))
- prints::
+ prints:
+
+ .. testoutput::
[datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)]
"""
@@ -329,15 +370,20 @@ class pyparsing_common:
def strip_html_tags(s: str, l: int, tokens: ParseResults):
"""Parse action to remove HTML tags from web page HTML source
- Example::
+ Example:
+
+ .. testcode::
# strip HTML links from normal text
text = '<td>More info at the <a href="https://github.com/pyparsing/pyparsing/wiki">pyparsing</a> wiki page</td>'
td, td_end = make_html_tags("TD")
- table_text = td + SkipTo(td_end).set_parse_action(pyparsing_common.strip_html_tags)("body") + td_end
+ table_text = td + SkipTo(td_end).set_parse_action(
+ pyparsing_common.strip_html_tags)("body") + td_end
print(table_text.parse_string(text).body)
- Prints::
+ Prints:
+
+ .. testoutput::
More info at the pyparsing wiki page
"""
@@ -414,7 +460,12 @@ class pyparsing_common:
r"(#(?P<fragment>\S*))?" +
r")"
).set_name("url")
- """URL (http/https/ftp scheme)"""
+ """
+ URL (http/https/ftp scheme)
+
+ .. versionchanged:: 3.1.0
+ ``url`` named group added
+ """
# fmt: on
# pre-PEP8 compatibility names
diff --git a/contrib/python/pyparsing/py3/pyparsing/core.py b/contrib/python/pyparsing/py3/pyparsing/core.py
index 86be949ad47..9c5894eb42a 100644
--- a/contrib/python/pyparsing/py3/pyparsing/core.py
+++ b/contrib/python/pyparsing/py3/pyparsing/core.py
@@ -324,14 +324,15 @@ def condition_as_parse_action(
"""
Function to convert a simple predicate function that returns ``True`` or ``False``
into a parse action. Can be used in places when a parse action is required
- and :class:`ParserElement.add_condition` cannot be used (such as when adding a condition
+ and :meth:`ParserElement.add_condition` cannot be used (such as when adding a condition
to an operator level in :class:`infix_notation`).
Optional keyword arguments:
- - ``message`` - define a custom message to be used in the raised exception
- - ``fatal`` - if True, will raise :class:`ParseFatalException` to stop parsing immediately;
- otherwise will raise :class:`ParseException`
+ :param message: define a custom message to be used in the raised exception
+ :param fatal: if ``True``, will raise :class:`ParseFatalException`
+ to stop parsing immediately;
+ otherwise will raise :class:`ParseException`
"""
msg = message if message is not None else "failed user-defined condition"
@@ -398,14 +399,21 @@ class ParserElement(ABC):
r"""
Overrides the default whitespace chars
- Example::
+ Example:
+
+ .. doctest::
# default whitespace chars are space, <TAB> and newline
- Word(alphas)[1, ...].parse_string("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl']
+ >>> Word(alphas)[1, ...].parse_string("abc def\nghi jkl")
+ ParseResults(['abc', 'def', 'ghi', 'jkl'], {})
# change to just treat newline as significant
- ParserElement.set_default_whitespace_chars(" \t")
- Word(alphas)[1, ...].parse_string("abc def\nghi jkl") # -> ['abc', 'def']
+ >>> ParserElement.set_default_whitespace_chars(" \t")
+ >>> Word(alphas)[1, ...].parse_string("abc def\nghi jkl")
+ ParseResults(['abc', 'def'], {})
+
+ # Reset to default
+ >>> ParserElement.set_default_whitespace_chars(" \n\t\r")
"""
ParserElement.DEFAULT_WHITE_CHARS = chars
@@ -419,20 +427,37 @@ class ParserElement(ABC):
"""
Set class to be used for inclusion of string literals into a parser.
- Example::
+ Example:
- # default literal class used is Literal
- integer = Word(nums)
- date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+ .. doctest::
+ :options: +NORMALIZE_WHITESPACE
- date_str.parse_string("1999/12/31") # -> ['1999', '/', '12', '/', '31']
+ # default literal class used is Literal
+ >>> integer = Word(nums)
+ >>> date_str = (
+ ... integer("year") + '/'
+ ... + integer("month") + '/'
+ ... + integer("day")
+ ... )
+ >>> date_str.parse_string("1999/12/31")
+ ParseResults(['1999', '/', '12', '/', '31'],
+ {'year': '1999', 'month': '12', 'day': '31'})
# change to Suppress
- ParserElement.inline_literals_using(Suppress)
- date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+ >>> ParserElement.inline_literals_using(Suppress)
+ >>> date_str = (
+ ... integer("year") + '/'
+ ... + integer("month") + '/'
+ ... + integer("day")
+ ... )
- date_str.parse_string("1999/12/31") # -> ['1999', '12', '31']
+ >>> date_str.parse_string("1999/12/31")
+ ParseResults(['1999', '12', '31'],
+ {'year': '1999', 'month': '12', 'day': '31'})
+
+ # Reset
+ >>> ParserElement.inline_literals_using(Literal)
"""
ParserElement._literalStringClass = cls
@@ -441,10 +466,13 @@ class ParserElement(ABC):
"""
Yields a sequence of ``class(obj, **class_kwargs)`` for obj in seq.
- Example::
+ Example:
+
+ .. testcode::
LPAR, RPAR, LBRACE, RBRACE, SEMI = Suppress.using_each("(){};")
+ .. versionadded:: 3.1.0
"""
yield from (cls(obj, **class_kwargs) for obj in seq)
@@ -494,14 +522,20 @@ class ParserElement(ABC):
"""
Suppress warnings emitted for a particular diagnostic on this expression.
- Example::
+ Example:
- base = pp.Forward()
- base.suppress_warning(Diagnostics.warn_on_parse_using_empty_Forward)
+ .. doctest::
- # statement would normally raise a warning, but is now suppressed
- print(base.parse_string("x"))
+ >>> label = pp.Word(pp.alphas)
+ # Normally using an empty Forward in a grammar
+ # would print a warning, but we can suppress that
+ >>> base = pp.Forward().suppress_warning(
+ ... pp.Diagnostics.warn_on_parse_using_empty_Forward)
+
+ >>> grammar = base | label
+ >>> print(grammar.parse_string("x"))
+ ['x']
"""
self.suppress_warnings_.append(warning_type)
return self
@@ -529,21 +563,34 @@ class ParserElement(ABC):
different parse actions for the same parsing pattern, using copies of
the original parse element.
- Example::
+ Example:
+
+ .. testcode::
+
+ integer = Word(nums).set_parse_action(
+ lambda toks: int(toks[0]))
+ integerK = integer.copy().add_parse_action(
+ lambda toks: toks[0] * 1024) + Suppress("K")
+ integerM = integer.copy().add_parse_action(
+ lambda toks: toks[0] * 1024 * 1024) + Suppress("M")
- integer = Word(nums).set_parse_action(lambda toks: int(toks[0]))
- integerK = integer.copy().add_parse_action(lambda toks: toks[0] * 1024) + Suppress("K")
- integerM = integer.copy().add_parse_action(lambda toks: toks[0] * 1024 * 1024) + Suppress("M")
+ print(
+ (integerK | integerM | integer)[1, ...].parse_string(
+ "5K 100 640K 256M")
+ )
- print((integerK | integerM | integer)[1, ...].parse_string("5K 100 640K 256M"))
+ prints:
- prints::
+ .. testoutput::
[5120, 100, 655360, 268435456]
- Equivalent form of ``expr.copy()`` is just ``expr()``::
+ Equivalent form of ``expr.copy()`` is just ``expr()``:
+
+ .. testcode::
- integerM = integer().add_parse_action(lambda toks: toks[0] * 1024 * 1024) + Suppress("M")
+ integerM = integer().add_parse_action(
+ lambda toks: toks[0] * 1024 * 1024) + Suppress("M")
"""
cpy = copy.copy(self)
cpy.parseAction = self.parseAction[:]
@@ -570,10 +617,12 @@ class ParserElement(ABC):
You can also set results names using the abbreviated syntax,
``expr("name")`` in place of ``expr.set_results_name("name")``
- - see :class:`__call__`. If ``list_all_matches`` is required, use
+ - see :meth:`__call__`. If ``list_all_matches`` is required, use
``expr("name*")``.
- Example::
+ Example:
+
+ .. testcode::
integer = Word(nums)
date_str = (integer.set_results_name("year") + '/'
@@ -646,29 +695,39 @@ class ParserElement(ABC):
Optional keyword arguments:
- - ``call_during_try`` = (default= ``False``) indicate if parse action should be run during
- lookaheads and alternate testing. For parse actions that have side effects, it is
- important to only call the parse action once it is determined that it is being
- called as part of a successful parse. For parse actions that perform additional
- validation, then call_during_try should be passed as True, so that the validation
- code is included in the preliminary "try" parses.
+ :param call_during_try: (default= ``False``) indicate if parse action
+ should be run during lookaheads and alternate
+ testing. For parse actions that have side
+ effects, it is important to only call the parse
+ action once it is determined that it is being
+ called as part of a successful parse.
+ For parse actions that perform additional
+ validation, then ``call_during_try`` should
+ be passed as True, so that the validation code
+ is included in the preliminary "try" parses.
- Note: the default parsing behavior is to expand tabs in the input string
- before starting the parsing process. See :class:`parse_string` for more
- information on parsing strings containing ``<TAB>`` s, and suggested
- methods to maintain a consistent view of the parsed string, the parse
- location, and line and column positions within the parsed string.
+ .. Note::
+ The default parsing behavior is to expand tabs in the input string
+ before starting the parsing process.
+ See :meth:`parse_string` for more information on parsing strings
+ containing ``<TAB>`` s, and suggested methods to maintain a
+ consistent view of the parsed string, the parse location, and
+ line and column positions within the parsed string.
- Example::
+ Example: Parse dates in the form ``YYYY/MM/DD``
+ -----------------------------------------------
- # parse dates in the form YYYY/MM/DD
+ Setup code:
+
+ .. testcode::
- # use parse action to convert toks from str to int at parse time
def convert_to_int(toks):
+ '''a parse action to convert toks from str to int
+ at parse time'''
return int(toks[0])
- # use a parse action to verify that the date is a valid date
def is_valid_date(instring, loc, toks):
+ '''a parse action to verify that the date is a valid date'''
from datetime import date
year, month, day = toks[::2]
try:
@@ -683,14 +742,30 @@ class ParserElement(ABC):
integer.set_parse_action(convert_to_int)
date_str.set_parse_action(is_valid_date)
- # note that integer fields are now ints, not strings
- date_str.run_tests('''
- # successful parse - note that integer fields were converted to ints
- 1999/12/31
+ Successful parse - note that integer fields are converted to ints:
- # fail - invalid date
- 1999/13/31
- ''')
+ .. testcode::
+
+ print(date_str.parse_string("1999/12/31"))
+
+ prints:
+
+ .. testoutput::
+
+ [1999, '/', 12, '/', 31]
+
+ Failure - invalid date:
+
+ .. testcode::
+
+ date_str.parse_string("1999/13/31")
+
+ prints:
+
+ .. testoutput::
+
+ Traceback (most recent call last):
+ ParseException: invalid date given, found '1999' ...
"""
if list(fns) == [None]:
self.parseAction.clear()
@@ -730,15 +805,20 @@ class ParserElement(ABC):
- ``call_during_try`` = boolean to indicate if this method should be called during internal tryParse calls,
default=False
- Example::
+ Example:
- integer = Word(nums).set_parse_action(lambda toks: int(toks[0]))
- year_int = integer.copy()
- year_int.add_condition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
- date_str = year_int + '/' + integer + '/' + integer
+ .. doctest::
+ :options: +NORMALIZE_WHITESPACE
- result = date_str.parse_string("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0),
- (line:1, col:1)
+ >>> integer = Word(nums).set_parse_action(lambda toks: int(toks[0]))
+ >>> year_int = integer.copy().add_condition(
+ ... lambda toks: toks[0] >= 2000,
+ ... message="Only support years 2000 and later")
+ >>> date_str = year_int + '/' + integer + '/' + integer
+
+ >>> result = date_str.parse_string("1999/12/31")
+ Traceback (most recent call last):
+ ParseException: Only support years 2000 and later...
"""
for fn in fns:
self.parseAction.append(
@@ -1029,12 +1109,14 @@ class ParserElement(ABC):
@staticmethod
def reset_cache() -> None:
- ParserElement.packrat_cache.clear()
- ParserElement.packrat_cache_stats[:] = [0] * len(
- ParserElement.packrat_cache_stats
- )
- ParserElement.recursion_memos.clear()
+ with ParserElement.packrat_cache_lock:
+ ParserElement.packrat_cache.clear()
+ ParserElement.packrat_cache_stats[:] = [0] * len(
+ ParserElement.packrat_cache_stats
+ )
+ ParserElement.recursion_memos.clear()
+ # class attributes to keep caching status
_packratEnabled = False
_left_recursion_enabled = False
@@ -1047,10 +1129,11 @@ class ParserElement(ABC):
This makes it safe to call before activating Packrat nor Left Recursion
to clear any previous settings.
"""
- ParserElement.reset_cache()
- ParserElement._left_recursion_enabled = False
- ParserElement._packratEnabled = False
- ParserElement._parse = ParserElement._parseNoCache
+ with ParserElement.packrat_cache_lock:
+ ParserElement.reset_cache()
+ ParserElement._left_recursion_enabled = False
+ ParserElement._packratEnabled = False
+ ParserElement._parse = ParserElement._parseNoCache
@staticmethod
def enable_left_recursion(
@@ -1062,17 +1145,26 @@ class ParserElement(ABC):
repeatedly matched with a fixed recursion depth that is gradually increased
until finding the longest match.
- Example::
+ Example:
+
+ .. testcode::
import pyparsing as pp
pp.ParserElement.enable_left_recursion()
E = pp.Forward("E")
num = pp.Word(pp.nums)
+
# match `num`, or `num '+' num`, or `num '+' num '+' num`, ...
E <<= E + '+' - num | num
- print(E.parse_string("1+2+3"))
+ print(E.parse_string("1+2+3+4"))
+
+ prints:
+
+ .. testoutput::
+
+ ['1', '+', '2', '+', '3', '+', '4']
Recursion search naturally memoizes matches of ``Forward`` elements and may
thus skip reevaluation of parse actions during backtracking. This may break
@@ -1088,17 +1180,18 @@ class ParserElement(ABC):
thus the two cannot be used together. Use ``force=True`` to disable any
previous, conflicting settings.
"""
- if force:
- ParserElement.disable_memoization()
- elif ParserElement._packratEnabled:
- raise RuntimeError("Packrat and Bounded Recursion are not compatible")
- if cache_size_limit is None:
- ParserElement.recursion_memos = _UnboundedMemo()
- elif cache_size_limit > 0:
- ParserElement.recursion_memos = _LRUMemo(capacity=cache_size_limit) # type: ignore[assignment]
- else:
- raise NotImplementedError(f"Memo size of {cache_size_limit}")
- ParserElement._left_recursion_enabled = True
+ with ParserElement.packrat_cache_lock:
+ if force:
+ ParserElement.disable_memoization()
+ elif ParserElement._packratEnabled:
+ raise RuntimeError("Packrat and Bounded Recursion are not compatible")
+ if cache_size_limit is None:
+ ParserElement.recursion_memos = _UnboundedMemo()
+ elif cache_size_limit > 0:
+ ParserElement.recursion_memos = _LRUMemo(capacity=cache_size_limit) # type: ignore[assignment]
+ else:
+ raise NotImplementedError(f"Memo size of {cache_size_limit}")
+ ParserElement._left_recursion_enabled = True
@staticmethod
def enable_packrat(
@@ -1125,6 +1218,8 @@ class ParserElement(ABC):
For best results, call ``enable_packrat()`` immediately after
importing pyparsing.
+ .. Can't really be doctested, alas
+
Example::
import pyparsing
@@ -1134,20 +1229,21 @@ class ParserElement(ABC):
thus the two cannot be used together. Use ``force=True`` to disable any
previous, conflicting settings.
"""
- if force:
- ParserElement.disable_memoization()
- elif ParserElement._left_recursion_enabled:
- raise RuntimeError("Packrat and Bounded Recursion are not compatible")
+ with ParserElement.packrat_cache_lock:
+ if force:
+ ParserElement.disable_memoization()
+ elif ParserElement._left_recursion_enabled:
+ raise RuntimeError("Packrat and Bounded Recursion are not compatible")
- if ParserElement._packratEnabled:
- return
+ if ParserElement._packratEnabled:
+ return
- ParserElement._packratEnabled = True
- if cache_size_limit is None:
- ParserElement.packrat_cache = _UnboundedCache()
- else:
- ParserElement.packrat_cache = _FifoCache(cache_size_limit)
- ParserElement._parse = ParserElement._parseCache
+ ParserElement._packratEnabled = True
+ if cache_size_limit is None:
+ ParserElement.packrat_cache = _UnboundedCache()
+ else:
+ ParserElement.packrat_cache = _FifoCache(cache_size_limit)
+ ParserElement._parse = ParserElement._parseCache
def parse_string(
self, instring: str, parse_all: bool = False, *, parseAll: bool = False
@@ -1180,19 +1276,22 @@ class ParserElement(ABC):
By default, partial matches are OK.
- >>> res = Word('a').parse_string('aaaaabaaa')
- >>> print(res)
- ['aaaaa']
+ .. doctest::
+
+ >>> res = Word('a').parse_string('aaaaabaaa')
+ >>> print(res)
+ ['aaaaa']
The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children
directly to see more examples.
It raises an exception if parse_all flag is set and instring does not match the whole grammar.
- >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True)
- Traceback (most recent call last):
- ...
- pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6)
+ .. doctest::
+
+ >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True)
+ Traceback (most recent call last):
+ ParseException: Expected end of text, found 'b' ...
"""
parseAll = parse_all or parseAll
@@ -1240,7 +1339,9 @@ class ParserElement(ABC):
being parsed. See :class:`parse_string` for more information on parsing
strings with embedded tabs.
- Example::
+ Example:
+
+ .. testcode::
source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
print(source)
@@ -1248,7 +1349,9 @@ class ParserElement(ABC):
print(' '*start + '^'*(end-start))
print(' '*start + tokens[0])
- prints::
+ prints:
+
+ .. testoutput::
sldjf123lsdjjkf345sldkjf879lkjsfd987
^^^^^
@@ -1327,16 +1430,24 @@ class ParserElement(ABC):
and replace the matched text patterns according to the logic in the parse
action. ``transform_string()`` returns the resulting transformed string.
- Example::
+ Example:
+
+ .. testcode::
+
+ quote = '''now is the winter of our discontent,
+ made glorious summer by this sun of york.'''
wd = Word(alphas)
wd.set_parse_action(lambda toks: toks[0].title())
- print(wd.transform_string("now is the winter of our discontent made glorious summer by this sun of york."))
+ print(wd.transform_string(quote))
+
+ prints:
- prints::
+ .. testoutput::
- Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
+ Now Is The Winter Of Our Discontent,
+ Made Glorious Summer By This Sun Of York.
"""
out: list[str] = []
lastE = 0
@@ -1382,17 +1493,26 @@ class ParserElement(ABC):
to match the given parse expression. May be called with optional
``max_matches`` argument, to clip searching after 'n' matches are found.
- Example::
+ Example:
+
+ .. testcode::
- # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
+ quote = '''More than Iron, more than Lead,
+ more than Gold I need Electricity'''
+
+ # a capitalized word starts with an uppercase letter,
+ # followed by zero or more lowercase letters
cap_word = Word(alphas.upper(), alphas.lower())
- print(cap_word.search_string("More than Iron, more than Lead, more than Gold I need Electricity"))
+ print(cap_word.search_string(quote))
+
+ # the sum() builtin can be used to merge results
+ # into a single ParseResults object
+ print(sum(cap_word.search_string(quote)))
- # the sum() builtin can be used to merge results into a single ParseResults object
- print(sum(cap_word.search_string("More than Iron, more than Lead, more than Gold I need Electricity")))
+ prints:
- prints::
+ .. testoutput::
[['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
@@ -1428,12 +1548,17 @@ class ParserElement(ABC):
and the optional ``include_separators`` argument (default= ``False``), if the separating
matching text should be included in the split results.
- Example::
+ Example:
+
+ .. testcode::
punc = one_of(list(".,;:/-!?"))
- print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
+ print(list(punc.split(
+ "This, this?, this sentence, is badly punctuated!")))
+
+ prints:
- prints::
+ .. testoutput::
['This', ' this', '', ' this sentence', ' is badly punctuated', '']
"""
@@ -1451,21 +1576,29 @@ class ParserElement(ABC):
Implementation of ``+`` operator - returns :class:`And`. Adding strings to a :class:`ParserElement`
converts them to :class:`Literal`\\ s by default.
- Example::
+ Example:
+
+ .. testcode::
greet = Word(alphas) + "," + Word(alphas) + "!"
hello = "Hello, World!"
print(hello, "->", greet.parse_string(hello))
- prints::
+ prints:
+
+ .. testoutput::
Hello, World! -> ['Hello', ',', 'World', '!']
- ``...`` may be used as a parse expression as a short form of :class:`SkipTo`::
+ ``...`` may be used as a parse expression as a short form of :class:`SkipTo`:
+
+ .. testcode::
Literal('start') + ... + Literal('end')
- is equivalent to::
+ is equivalent to:
+
+ .. testcode::
Literal('start') + SkipTo('end')("_skipped*") + Literal('end')
@@ -1601,6 +1734,9 @@ class ParserElement(ABC):
def __or__(self, other) -> ParserElement:
"""
Implementation of ``|`` operator - returns :class:`MatchFirst`
+
+ .. versionchanged:: 3.1.0
+ Support ``expr | ""`` as a synonym for ``Optional(expr)``.
"""
if other is Ellipsis:
return _PendingSkip(self, must_skip=True)
@@ -1699,6 +1835,8 @@ class ParserElement(ABC):
- ``expr[...: end_expr]`` and ``expr[0, ...: end_expr]`` are equivalent to ``ZeroOrMore(expr, stop_on=end_expr)``
- ``expr[1, ...: end_expr]`` is equivalent to ``OneOrMore(expr, stop_on=end_expr)``
+ .. versionchanged:: 3.1.0
+ Support for slice notation.
"""
stop_on_defined = False
@@ -1743,10 +1881,16 @@ class ParserElement(ABC):
If ``name`` is omitted, same as calling :class:`copy`.
- Example::
+ Example:
+
+ .. testcode::
# these are equivalent
- userdata = Word(alphas).set_results_name("name") + Word(nums + "-").set_results_name("socsecno")
+ userdata = (
+ Word(alphas).set_results_name("name")
+ + Word(nums + "-").set_results_name("socsecno")
+ )
+
userdata = Word(alphas)("name") + Word(nums + "-")("socsecno")
"""
if name is not None:
@@ -1808,15 +1952,17 @@ class ParserElement(ABC):
matching; may be called repeatedly, to define multiple comment or other
ignorable patterns.
- Example::
+ Example:
+
+ .. doctest::
- patt = Word(alphas)[...]
- patt.parse_string('ablaj /* comment */ lskjd')
- # -> ['ablaj']
+ >>> patt = Word(alphas)[...]
+ >>> print(patt.parse_string('ablaj /* comment */ lskjd'))
+ ['ablaj']
- patt.ignore(c_style_comment)
- patt.parse_string('ablaj /* comment */ lskjd')
- # -> ['ablaj', 'lskjd']
+ >>> patt = Word(alphas)[...].ignore(c_style_comment)
+ >>> print(patt.parse_string('ablaj /* comment */ lskjd'))
+ ['ablaj', 'lskjd']
"""
if isinstance(other, str_type):
other = Suppress(other)
@@ -1837,14 +1983,32 @@ class ParserElement(ABC):
"""
Customize display of debugging messages while doing pattern matching:
- - ``start_action`` - method to be called when an expression is about to be parsed;
- should have the signature ``fn(input_string: str, location: int, expression: ParserElement, cache_hit: bool)``
+ :param start_action: method to be called when an expression is about to be parsed;
+ should have the signature::
+
+ fn(input_string: str,
+ location: int,
+ expression: ParserElement,
+ cache_hit: bool)
- - ``success_action`` - method to be called when an expression has successfully parsed;
- should have the signature ``fn(input_string: str, start_location: int, end_location: int, expression: ParserELement, parsed_tokens: ParseResults, cache_hit: bool)``
+ :param success_action: method to be called when an expression has successfully parsed;
+ should have the signature::
+
+ fn(input_string: str,
+ start_location: int,
+ end_location: int,
+ expression: ParserELement,
+ parsed_tokens: ParseResults,
+ cache_hit: bool)
- - ``exception_action`` - method to be called when expression fails to parse;
- should have the signature ``fn(input_string: str, location: int, expression: ParserElement, exception: Exception, cache_hit: bool)``
+ :param exception_action: method to be called when expression fails to parse;
+ should have the signature::
+
+ fn(input_string: str,
+ location: int,
+ expression: ParserElement,
+ exception: Exception,
+ cache_hit: bool)
"""
self.debugActions = self.DebugActions(
start_action or _default_start_debug_action, # type: ignore[truthy-function]
@@ -1860,7 +2024,9 @@ class ParserElement(ABC):
Set ``flag`` to ``True`` to enable, ``False`` to disable.
Set ``recurse`` to ``True`` to set the debug flag on this expression and all sub-expressions.
- Example::
+ Example:
+
+ .. testcode::
wd = Word(alphas).set_name("alphaword")
integer = Word(nums).set_name("numword")
@@ -1871,26 +2037,41 @@ class ParserElement(ABC):
term[1, ...].parse_string("abc 123 xyz 890")
- prints::
+ prints:
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
Match alphaword at loc 0(1,1)
+ abc 123 xyz 890
+ ^
Matched alphaword -> ['abc']
- Match alphaword at loc 3(1,4)
- Exception raised:Expected alphaword (at char 4), (line:1, col:5)
- Match alphaword at loc 7(1,8)
+ Match alphaword at loc 4(1,5)
+ abc 123 xyz 890
+ ^
+ Match alphaword failed, ParseException raised: Expected alphaword, ...
+ Match alphaword at loc 8(1,9)
+ abc 123 xyz 890
+ ^
Matched alphaword -> ['xyz']
- Match alphaword at loc 11(1,12)
- Exception raised:Expected alphaword (at char 12), (line:1, col:13)
- Match alphaword at loc 15(1,16)
- Exception raised:Expected alphaword (at char 15), (line:1, col:16)
+ Match alphaword at loc 12(1,13)
+ abc 123 xyz 890
+ ^
+ Match alphaword failed, ParseException raised: Expected alphaword, ...
+ abc 123 xyz 890
+ ^
+ Match alphaword failed, ParseException raised: Expected alphaword, found end of text ...
The output shown is that produced by the default debug actions - custom debug actions can be
- specified using :class:`set_debug_actions`. Prior to attempting
+ specified using :meth:`set_debug_actions`. Prior to attempting
to match the ``wd`` expression, the debugging message ``"Match <exprname> at loc <n>(<line>,<col>)"``
is shown. Then if the parse succeeds, a ``"Matched"`` message is shown, or an ``"Exception raised"``
- message is shown. Also note the use of :class:`set_name` to assign a human-readable name to the expression,
+ message is shown. Also note the use of :meth:`set_name` to assign a human-readable name to the expression,
which makes debugging and exception messages easier to understand - for instance, the default
- name created for the :class:`Word` expression without calling ``set_name`` is ``"W:(A-Za-z)"``.
+ name created for the :class:`Word` expression without calling :meth:`set_name` is ``"W:(A-Za-z)"``.
+
+ .. versionchanged:: 3.1.0
+ ``recurse`` argument added.
"""
if recurse:
for expr in self.visit_all():
@@ -1928,13 +2109,23 @@ class ParserElement(ABC):
If `name` is None, clears any custom name for this expression, and clears the
debug flag is it was enabled via `__diag__.enable_debug_on_named_expressions`.
- Example::
+ Example:
- integer = Word(nums)
- integer.parse_string("ABC") # -> Exception: Expected W:(0-9) (at char 0), (line:1, col:1)
+ .. doctest::
- integer.set_name("integer")
- integer.parse_string("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1)
+ >>> integer = Word(nums)
+ >>> integer.parse_string("ABC")
+ Traceback (most recent call last):
+ ParseException: Expected W:(0-9) (at char 0), (line:1, col:1)
+
+ >>> integer.set_name("integer")
+ integer
+ >>> integer.parse_string("ABC")
+ Traceback (most recent call last):
+ ParseException: Expected integer (at char 0), (line:1, col:1)
+
+ .. versionchanged:: 3.1.0
+ Accept ``None`` as the ``name`` argument.
"""
self.customName = name # type: ignore[assignment]
self.errmsg = f"Expected {str(self)}"
@@ -1974,7 +2165,11 @@ class ParserElement(ABC):
def validate(self, validateTrace=None) -> None:
"""
+ .. deprecated:: 3.0.0
+ Do not use to check for left recursion.
+
Check defined expressions for valid structure, check for infinite recursive definitions.
+
"""
warnings.warn(
"ParserElement.validate() is deprecated, and should not be used to check for left recursion",
@@ -2032,15 +2227,16 @@ class ParserElement(ABC):
Method for quick testing of a parser against a test string. Good for simple
inline microtests of sub expressions while building up larger parser.
- Parameters:
+ :param test_string: to test against this expression for a match
+ :param parse_all: flag to pass to :meth:`parse_string` when running tests
- - ``test_string`` - to test against this expression for a match
- - ``parse_all`` - (default= ``True``) - flag to pass to :class:`parse_string` when running tests
+ Example:
- Example::
+ .. doctest::
- expr = Word(nums)
- assert expr.matches("100")
+ >>> expr = Word(nums)
+ >>> expr.matches("100")
+ True
"""
parseAll = parseAll and parse_all
try:
@@ -2096,7 +2292,9 @@ class ParserElement(ABC):
(or failed if ``failure_tests`` is True), and the results contain a list of lines of each
test's output
- Example::
+ Passing example:
+
+ .. testcode::
number_expr = pyparsing_common.number.copy()
@@ -2109,20 +2307,16 @@ class ParserElement(ABC):
6.02e23
# integer with scientific notation
1e-12
+ # negative decimal number without leading digit
+ -.100
''')
print("Success" if result[0] else "Failed!")
- result = number_expr.run_tests('''
- # stray character
- 100Z
- # missing leading digit before '.'
- -.100
- # too many '.'
- 3.14.159
- ''', failure_tests=True)
- print("Success" if result[0] else "Failed!")
+ prints:
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
- prints::
# unsigned integer
100
@@ -2140,30 +2334,59 @@ class ParserElement(ABC):
1e-12
[1e-12]
+ # negative decimal number without leading digit
+ -.100
+ [-0.1]
Success
+ Failure-test example:
+
+ .. testcode::
+
+ result = number_expr.run_tests('''
+ # stray character
+ 100Z
+ # too many '.'
+ 3.14.159
+ ''', failure_tests=True)
+ print("Success" if result[0] else "Failed!")
+
+ prints:
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+
+
# stray character
100Z
+ 100Z
^
- FAIL: Expected end of text (at char 3), (line:1, col:4)
-
- # missing leading digit before '.'
- -.100
- ^
- FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
+ ParseException: Expected end of text, found 'Z' ...
# too many '.'
3.14.159
+ 3.14.159
^
- FAIL: Expected end of text (at char 4), (line:1, col:5)
-
+ ParseException: Expected end of text, found '.' ...
+ FAIL: Expected end of text, found '.' ...
Success
Each test string must be on a single line. If you want to test a string that spans multiple
- lines, create a test like this::
+ lines, create a test like this:
+ .. testcode::
+
+ expr = Word(alphanums)[1,...]
expr.run_tests(r"this is a test\\n of strings that spans \\n 3 lines")
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+ :hide:
+
+
+ this is a test\\n of strings that spans \\n 3 lines
+ ['this', 'is', 'a', 'test', 'of', 'strings', 'that', 'spans', '3', 'lines']
+
(Note that this is a raw string literal, you must include the leading ``'r'``.)
"""
from .testing import pyparsing_test
@@ -2296,6 +2519,9 @@ class ParserElement(ABC):
Additional diagram-formatting keyword arguments can also be included;
see railroad.Diagram class.
+
+ .. versionchanged:: 3.1.0
+ ``embed`` argument added.
"""
try:
@@ -2431,11 +2657,17 @@ class Literal(Token):
"""
Token to exactly match a specified string.
- Example::
+ Example:
- Literal('abc').parse_string('abc') # -> ['abc']
- Literal('abc').parse_string('abcdef') # -> ['abc']
- Literal('abc').parse_string('ab') # -> Exception: Expected "abc"
+ .. doctest::
+
+ >>> Literal('abc').parse_string('abc')
+ ParseResults(['abc'], {})
+ >>> Literal('abc').parse_string('abcdef')
+ ParseResults(['abc'], {})
+ >>> Literal('abc').parse_string('ab')
+ Traceback (most recent call last):
+ ParseException: Expected 'abc', found 'ab' (at char 0), (line: 1, col: 1)
For case-insensitive matching, use :class:`CaselessLiteral`.
@@ -2526,10 +2758,25 @@ class Keyword(Token):
"$"
- ``caseless`` allows case-insensitive matching, default is ``False``.
- Example::
+ Example:
+
+ .. doctest::
+ :options: +NORMALIZE_WHITESPACE
- Keyword("start").parse_string("start") # -> ['start']
- Keyword("start").parse_string("starting") # -> Exception
+ >>> Keyword("start").parse_string("start")
+ ParseResults(['start'], {})
+ >>> Keyword("start").parse_string("starting")
+ Traceback (most recent call last):
+ ParseException: Expected Keyword 'start', keyword was immediately
+ followed by keyword character, found 'ing' (at char 5), (line:1, col:6)
+
+ .. doctest::
+ :options: +NORMALIZE_WHITESPACE
+
+ >>> Keyword("start").parse_string("starting").debug()
+ Traceback (most recent call last):
+ ParseException: Expected Keyword "start", keyword was immediately
+ followed by keyword character, found 'ing' ...
For case-insensitive matching, use :class:`CaselessKeyword`.
"""
@@ -2630,10 +2877,12 @@ class CaselessLiteral(Literal):
Note: the matched results will always be in the case of the given
match string, NOT the case of the input text.
- Example::
+ Example:
+
+ .. doctest::
- CaselessLiteral("CMD")[1, ...].parse_string("cmd CMD Cmd10")
- # -> ['CMD', 'CMD', 'CMD']
+ >>> CaselessLiteral("CMD")[1, ...].parse_string("cmd CMD Cmd10")
+ ParseResults(['CMD', 'CMD', 'CMD'], {})
(Contrast with example for :class:`CaselessKeyword`.)
"""
@@ -2655,10 +2904,12 @@ class CaselessKeyword(Keyword):
"""
Caseless version of :class:`Keyword`.
- Example::
+ Example:
- CaselessKeyword("CMD")[1, ...].parse_string("cmd CMD Cmd10")
- # -> ['CMD', 'CMD']
+ .. doctest::
+
+ >>> CaselessKeyword("CMD")[1, ...].parse_string("cmd CMD Cmd10")
+ ParseResults(['CMD', 'CMD'], {})
(Contrast with example for :class:`CaselessLiteral`.)
"""
@@ -2697,18 +2948,31 @@ class CloseMatch(Token):
If ``mismatches`` is an empty list, then the match was an exact
match.
- Example::
+ Example:
+
+ .. doctest::
+ :options: +NORMALIZE_WHITESPACE
- patt = CloseMatch("ATCATCGAATGGA")
- patt.parse_string("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
- patt.parse_string("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
+ >>> patt = CloseMatch("ATCATCGAATGGA")
+ >>> patt.parse_string("ATCATCGAAXGGA")
+ ParseResults(['ATCATCGAAXGGA'],
+ {'original': 'ATCATCGAATGGA', 'mismatches': [9]})
+
+ >>> patt.parse_string("ATCAXCGAAXGGA")
+ Traceback (most recent call last):
+ ParseException: Expected 'ATCATCGAATGGA' (with up to 1 mismatches),
+ found 'ATCAXCGAAXGGA' (at char 0), (line:1, col:1)
# exact match
- patt.parse_string("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
+ >>> patt.parse_string("ATCATCGAATGGA")
+ ParseResults(['ATCATCGAATGGA'],
+ {'original': 'ATCATCGAATGGA', 'mismatches': []})
# close match allowing up to 2 mismatches
- patt = CloseMatch("ATCATCGAATGGA", max_mismatches=2)
- patt.parse_string("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
+ >>> patt = CloseMatch("ATCATCGAATGGA", max_mismatches=2)
+ >>> patt.parse_string("ATCAXCGAAXGGA")
+ ParseResults(['ATCAXCGAAXGGA'],
+ {'original': 'ATCATCGAATGGA', 'mismatches': [4, 9]})
"""
def __init__(
@@ -2799,23 +3063,28 @@ class Word(Token):
pyparsing includes helper strings for building Words:
- - :class:`alphas`
- - :class:`nums`
- - :class:`alphanums`
- - :class:`hexnums`
- - :class:`alphas8bit` (alphabetic characters in ASCII range 128-255
+ - :attr:`alphas`
+ - :attr:`nums`
+ - :attr:`alphanums`
+ - :attr:`hexnums`
+ - :attr:`alphas8bit` (alphabetic characters in ASCII range 128-255
- accented, tilded, umlauted, etc.)
- - :class:`punc8bit` (non-alphabetic characters in ASCII range
+ - :attr:`punc8bit` (non-alphabetic characters in ASCII range
128-255 - currency, symbols, superscripts, diacriticals, etc.)
- - :class:`printables` (any non-whitespace character)
+ - :attr:`printables` (any non-whitespace character)
``alphas``, ``nums``, and ``printables`` are also defined in several
- Unicode sets - see :class:`pyparsing_unicode``.
+ Unicode sets - see :class:`pyparsing_unicode`.
- Example::
+ Example:
+
+ .. testcode::
# a word composed of digits
- integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
+ integer = Word(nums)
+ # Two equivalent alternate forms:
+ Word("0123456789")
+ Word(srange("[0-9]"))
# a word with a leading capital, and zero or more lowercase
capitalized_word = Word(alphas.upper(), alphas.lower())
@@ -2823,11 +3092,18 @@ class Word(Token):
# hostnames are alphanumeric, with leading alpha, and '-'
hostname = Word(alphas, alphanums + '-')
- # roman numeral (not a strict parser, accepts invalid mix of characters)
+ # roman numeral
+ # (not a strict parser, accepts invalid mix of characters)
roman = Word("IVXLCDM")
# any string of non-whitespace characters, except for ','
csv_value = Word(printables, exclude_chars=",")
+
+ :raises ValueError: If ``min`` and ``max`` are both specified
+ and the test ``min <= max`` fails.
+
+ .. versionchanged:: 3.1.0
+ Raises :exc:`ValueError` if ``min`` > ``max``.
"""
def __init__(
@@ -2948,6 +3224,13 @@ class Word(Token):
self.re_match = self.re.match
self.parseImpl = self.parseImpl_regex # type: ignore[method-assign]
+ def copy(self) -> Word:
+ ret: Word = cast(Word, super().copy())
+ if hasattr(self, "re_match"):
+ ret.re_match = self.re_match
+ ret.parseImpl = ret.parseImpl_regex # type: ignore[method-assign]
+ return ret
+
def _generateDefaultName(self) -> str:
def charsAsStr(s):
max_repr_len = 16
@@ -3047,7 +3330,14 @@ class Regex(Token):
(such as the ``regex`` module), you can do so by building your ``Regex`` object with
a compiled RE that was compiled using ``regex``.
- Example::
+ The parameters ``pattern`` and ``flags`` are passed
+ to the ``re.compile()`` function as-is. See the Python
+ `re module <https://docs.python.org/3/library/re.html>`_ module for an
+ explanation of the acceptable patterns and flags.
+
+ Example:
+
+ .. testcode::
realnum = Regex(r"[+-]?\d+\.\d*")
# ref: https://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
@@ -3056,9 +3346,10 @@ class Regex(Token):
# named fields in a regex will be returned as named results
date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)')
- # the Regex class will accept re's compiled using the regex module
- import regex
- parser = pp.Regex(regex.compile(r'[0-9]'))
+ # the Regex class will accept regular expressions compiled using the
+ # re module
+ import re
+ parser = pp.Regex(re.compile(r'[0-9]'))
"""
def __init__(
@@ -3071,11 +3362,6 @@ class Regex(Token):
asGroupList: bool = False,
asMatch: bool = False,
) -> None:
- """The parameters ``pattern`` and ``flags`` are passed
- to the ``re.compile()`` function as-is. See the Python
- `re module <https://docs.python.org/3/library/re.html>`_ module for an
- explanation of the acceptable patterns and flags.
- """
super().__init__()
asGroupList = asGroupList or as_group_list
asMatch = asMatch or as_match
@@ -3116,6 +3402,14 @@ class Regex(Token):
if self.asMatch:
self.parseImpl = self.parseImplAsMatch # type: ignore [method-assign]
+ def copy(self):
+ ret: Regex = cast(Regex, super().copy())
+ if self.asGroupList:
+ ret.parseImpl = ret.parseImplAsGroupList
+ if self.asMatch:
+ ret.parseImpl = ret.parseImplAsMatch
+ return ret
+
@cached_property
def re(self) -> re.Pattern:
if self._re:
@@ -3207,11 +3501,16 @@ class Regex(Token):
Return :class:`Regex` with an attached parse action to transform the parsed
result as if called using `re.sub(expr, repl, string) <https://docs.python.org/3/library/re.html#re.sub>`_.
- Example::
+ Example:
+
+ .. testcode::
make_html = Regex(r"(\w+):(.*?):").sub(r"<\1>\2</\1>")
print(make_html.transform_string("h1:main title:"))
- # prints "<h1>main title</h1>"
+
+ .. testoutput::
+
+ <h1>main title</h1>
"""
if self.asGroupList:
raise TypeError("cannot use sub() with Regex(as_group_list=True)")
@@ -3258,19 +3557,23 @@ class QuotedString(Token):
(``'\t'``, ``'\n'``, etc.) to actual whitespace
(default= ``True``)
- Example::
+ .. caution:: ``convert_whitespace_escapes`` has no effect if
+ ``unquote_results`` is ``False``.
- qs = QuotedString('"')
- print(qs.search_string('lsjdf "This is the quote" sldjf'))
- complex_qs = QuotedString('{{', end_quote_char='}}')
- print(complex_qs.search_string('lsjdf {{This is the "quote"}} sldjf'))
- sql_qs = QuotedString('"', esc_quote='""')
- print(sql_qs.search_string('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
+ Example:
- prints::
+ .. doctest::
+ >>> qs = QuotedString('"')
+ >>> print(qs.search_string('lsjdf "This is the quote" sldjf'))
[['This is the quote']]
+ >>> complex_qs = QuotedString('{{', end_quote_char='}}')
+ >>> print(complex_qs.search_string(
+ ... 'lsjdf {{This is the "quote"}} sldjf'))
[['This is the "quote"']]
+ >>> sql_qs = QuotedString('"', esc_quote='""')
+ >>> print(sql_qs.search_string(
+ ... 'lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
[['This is the quote with "embedded" quotes']]
"""
@@ -3480,13 +3783,21 @@ class CharsNotIn(Token):
``max`` and ``exact`` are 0, meaning no maximum or exact
length restriction.
- Example::
+ Example:
+
+ .. testcode::
# define a comma-separated-value as anything that is not a ','
csv_value = CharsNotIn(',')
- print(DelimitedList(csv_value).parse_string("dkls,lsdkjf,s12 34,@!#,213"))
+ print(
+ DelimitedList(csv_value).parse_string(
+ "dkls,lsdkjf,s12 34,@!#,213"
+ )
+ )
- prints::
+ prints:
+
+ .. testoutput::
['dkls', 'lsdkjf', 's12 34', '@!#', '213']
"""
@@ -3674,22 +3985,27 @@ class LineStart(PositionToken):
r"""Matches if current position is at the beginning of a line within
the parse string
- Example::
+ Example:
+
+ .. testcode::
test = '''\
AAA this line
AAA and this line
- AAA but not this one
- B AAA and definitely not this one
+ AAA and even this line
+ B AAA but definitely not this line
'''
for t in (LineStart() + 'AAA' + rest_of_line).search_string(test):
print(t)
- prints::
+ prints:
+
+ .. testoutput::
['AAA', ' this line']
['AAA', ' and this line']
+ ['AAA', ' and even this line']
"""
@@ -3843,23 +4159,23 @@ class Tag(Token):
processing the parsed results. Accepts an optional tag value,
defaulting to `True`.
- Example::
-
- end_punc = "." | ("!" + Tag("enthusiastic")))
- greeting = "Hello," + Word(alphas) + end_punc
+ Example:
- result = greeting.parse_string("Hello, World.")
- print(result.dump())
+ .. doctest::
- result = greeting.parse_string("Hello, World!")
- print(result.dump())
-
- prints::
+ >>> end_punc = "." | ("!" + Tag("enthusiastic"))
+ >>> greeting = "Hello," + Word(alphas) + end_punc
+ >>> result = greeting.parse_string("Hello, World.")
+ >>> print(result.dump())
['Hello,', 'World', '.']
+ >>> result = greeting.parse_string("Hello, World!")
+ >>> print(result.dump())
['Hello,', 'World', '!']
- enthusiastic: True
+
+ .. versionadded:: 3.1.0
"""
def __init__(self, tag_name: str, value: Any = True) -> None:
@@ -4060,7 +4376,9 @@ class And(ParseExpression):
May also be constructed using the ``'-'`` operator, which will
suppress backtracking.
- Example::
+ Example:
+
+ .. testcode::
integer = Word(nums)
name_expr = Word(alphas)[1, ...]
@@ -4226,14 +4544,18 @@ class Or(ParseExpression):
string will be used. May be constructed using the ``'^'``
operator.
- Example::
+ Example:
+
+ .. testcode::
# construct Or using '^' operator
number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums))
print(number.search_string("123 3.1416 789"))
- prints::
+ prints:
+
+ .. testoutput::
[['123'], ['3.1416'], ['789']]
"""
@@ -4383,17 +4705,19 @@ class MatchFirst(ParseExpression):
more than one expression matches, the first one listed is the one that will
match. May be constructed using the ``'|'`` operator.
- Example::
+ Example: Construct MatchFirst using '|' operator
- # construct MatchFirst using '|' operator
+ .. doctest::
# watch the order of expressions to match
- number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
- print(number.search_string("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']]
+ >>> number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
+ >>> print(number.search_string("123 3.1416 789")) # Fail!
+ [['123'], ['3'], ['1416'], ['789']]
# put more selective expression first
- number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
- print(number.search_string("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']]
+ >>> number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
+ >>> print(number.search_string("123 3.1416 789")) # Better
+ [['123'], ['3.1416'], ['789']]
"""
def __init__(
@@ -4494,7 +4818,9 @@ class Each(ParseExpression):
May be constructed using the ``'&'`` operator.
- Example::
+ Example:
+
+ .. testcode::
color = one_of("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
shape_type = one_of("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
@@ -4515,35 +4841,42 @@ class Each(ParseExpression):
'''
)
- prints::
+ prints:
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+
shape: SQUARE color: BLACK posn: 100, 120
['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
- - color: BLACK
+ - color: 'BLACK'
- posn: ['100', ',', '120']
- - x: 100
- - y: 120
- - shape: SQUARE
-
+ - x: '100'
+ - y: '120'
+ - shape: 'SQUARE'
+ ...
shape: CIRCLE size: 50 color: BLUE posn: 50,80
- ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
- - color: BLUE
+ ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE',
+ 'posn:', ['50', ',', '80']]
+ - color: 'BLUE'
- posn: ['50', ',', '80']
- - x: 50
- - y: 80
- - shape: CIRCLE
- - size: 50
-
+ - x: '50'
+ - y: '80'
+ - shape: 'CIRCLE'
+ - size: '50'
+ ...
- color: GREEN size: 20 shape: TRIANGLE posn: 20,40
- ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
- - color: GREEN
+ color:GREEN size:20 shape:TRIANGLE posn:20,40
+ ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE',
+ 'posn:', ['20', ',', '40']]
+ - color: 'GREEN'
- posn: ['20', ',', '40']
- - x: 20
- - y: 40
- - shape: TRIANGLE
- - size: 20
+ - x: '20'
+ - y: '40'
+ - shape: 'TRIANGLE'
+ - size: '20'
+ ...
"""
def __init__(
@@ -4863,23 +5196,26 @@ class AtLineStart(ParseElementEnhance):
r"""Matches if an expression matches at the beginning of a line within
the parse string
- Example::
+ Example:
+
+ .. testcode::
test = '''\
- AAA this line
- AAA and this line
- AAA but not this one
- B AAA and definitely not this one
+ BBB this line
+ BBB and this line
+ BBB but not this one
+ A BBB and definitely not this one
'''
- for t in (AtLineStart('AAA') + rest_of_line).search_string(test):
+ for t in (AtLineStart('BBB') + rest_of_line).search_string(test):
print(t)
- prints::
+ prints:
- ['AAA', ' this line']
- ['AAA', ' and this line']
+ .. testoutput::
+ ['BBB', ' this line']
+ ['BBB', ' and this line']
"""
def __init__(self, expr: Union[ParserElement, str]) -> None:
@@ -4901,16 +5237,24 @@ class FollowedBy(ParseElementEnhance):
in the lookahead expression, those *will* be returned for access by
name.
- Example::
+ Example:
+
+ .. testcode::
# use FollowedBy to match a label only if it is followed by a ':'
data_word = Word(alphas)
label = data_word + FollowedBy(':')
- attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join))
+ attr_expr = Group(
+ label + Suppress(':')
+ + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)
+ )
- attr_expr[1, ...].parse_string("shape: SQUARE color: BLACK posn: upper left").pprint()
+ attr_expr[1, ...].parse_string(
+ "shape: SQUARE color: BLACK posn: upper left").pprint()
- prints::
+ prints:
+
+ .. testoutput::
[['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
"""
@@ -4950,12 +5294,13 @@ class PrecededBy(ParseElementEnhance):
give a maximum number of characters to look back from
the current parse position for a lookbehind match.
- Example::
+ Example:
+
+ .. testcode::
# VB-style variable names with type prefixes
int_var = PrecededBy("#") + pyparsing_common.identifier
str_var = PrecededBy("$") + pyparsing_common.identifier
-
"""
def __init__(self, expr: Union[ParserElement, str], retreat: int = 0) -> None:
@@ -5023,18 +5368,21 @@ class Located(ParseElementEnhance):
Be careful if the input text contains ``<TAB>`` characters, you
may want to call :class:`ParserElement.parse_with_tabs`
- Example::
+ Example:
+
+ .. testcode::
wd = Word(alphas)
for match in Located(wd).search_string("ljsdf123lksdjjf123lkkjj1222"):
print(match)
- prints::
+ prints:
+
+ .. testoutput::
[0, ['ljsdf'], 5]
[8, ['lksdjjf'], 15]
[18, ['lkkjj'], 23]
-
"""
def parseImpl(self, instring, loc, do_actions=True) -> ParseImplReturnType:
@@ -5060,7 +5408,9 @@ class NotAny(ParseElementEnhance):
*not* skip over leading whitespace. ``NotAny`` always returns
a null token list. May be constructed using the ``'~'`` operator.
- Example::
+ Example:
+
+ .. testcode::
AND, OR, NOT = map(CaselessKeyword, "AND OR NOT".split())
@@ -5182,21 +5532,34 @@ class OneOrMore(_MultipleMatch):
(only required if the sentinel would ordinarily match the repetition
expression)
- Example::
+ Example:
- data_word = Word(alphas)
- label = data_word + FollowedBy(':')
- attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).set_parse_action(' '.join))
+ .. doctest::
- text = "shape: SQUARE posn: upper left color: BLACK"
- attr_expr[1, ...].parse_string(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
+ >>> data_word = Word(alphas)
+ >>> label = data_word + FollowedBy(':')
+ >>> attr_expr = Group(
+ ... label + Suppress(':')
+ ... + OneOrMore(data_word).set_parse_action(' '.join))
- # use stop_on attribute for OneOrMore to avoid reading label string as part of the data
- attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join))
- OneOrMore(attr_expr).parse_string(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
+ >>> text = "shape: SQUARE posn: upper left color: BLACK"
+
+ # Fail! read 'posn' as data instead of next label
+ >>> attr_expr[1, ...].parse_string(text).pprint()
+ [['shape', 'SQUARE posn']]
+
+ # use stop_on attribute for OneOrMore
+ # to avoid reading label string as part of the data
+ >>> attr_expr = Group(
+ ... label + Suppress(':')
+ ... + OneOrMore(
+ ... data_word, stop_on=label).set_parse_action(' '.join))
+ >>> OneOrMore(attr_expr).parse_string(text).pprint() # Better
+ [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
# could also be written as
- (attr_expr * (1,)).parse_string(text).pprint()
+ >>> (attr_expr * (1,)).parse_string(text).pprint()
+ [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
"""
def _generateDefaultName(self) -> str:
@@ -5238,6 +5601,31 @@ class ZeroOrMore(_MultipleMatch):
class DelimitedList(ParseElementEnhance):
+ """Helper to define a delimited list of expressions - the delimiter
+ defaults to ','. By default, the list elements and delimiters can
+ have intervening whitespace, and comments, but this can be
+ overridden by passing ``combine=True`` in the constructor. If
+ ``combine`` is set to ``True``, the matching tokens are
+ returned as a single token string, with the delimiters included;
+ otherwise, the matching tokens are returned as a list of tokens,
+ with the delimiters suppressed.
+
+ If ``allow_trailing_delim`` is set to True, then the list may end with
+ a delimiter.
+
+ Example:
+
+ .. doctest::
+
+ >>> DelimitedList(Word(alphas)).parse_string("aa,bb,cc")
+ ParseResults(['aa', 'bb', 'cc'], {})
+ >>> DelimitedList(Word(hexnums), delim=':', combine=True
+ ... ).parse_string("AA:BB:CC:DD:EE")
+ ParseResults(['AA:BB:CC:DD:EE'], {})
+
+ .. versionadded:: 3.1.0
+ """
+
def __init__(
self,
expr: Union[str, ParserElement],
@@ -5248,23 +5636,6 @@ class DelimitedList(ParseElementEnhance):
*,
allow_trailing_delim: bool = False,
) -> None:
- """Helper to define a delimited list of expressions - the delimiter
- defaults to ','. By default, the list elements and delimiters can
- have intervening whitespace, and comments, but this can be
- overridden by passing ``combine=True`` in the constructor. If
- ``combine`` is set to ``True``, the matching tokens are
- returned as a single token string, with the delimiters included;
- otherwise, the matching tokens are returned as a list of tokens,
- with the delimiters suppressed.
-
- If ``allow_trailing_delim`` is set to True, then the list may end with
- a delimiter.
-
- Example::
-
- DelimitedList(Word(alphas)).parse_string("aa,bb,cc") # -> ['aa', 'bb', 'cc']
- DelimitedList(Word(hexnums), delim=':', combine=True).parse_string("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
- """
if isinstance(expr, str_type):
expr = ParserElement._literalStringClass(expr)
expr = typing.cast(ParserElement, expr)
@@ -5314,12 +5685,13 @@ class Opt(ParseElementEnhance):
"""
Optional matching of the given expression.
- Parameters:
+ :param expr: expression that must match zero or more times
+ :param default: (optional) - value to be returned
+ if the optional expression is not found.
- - ``expr`` - expression that must match zero or more times
- - ``default`` (optional) - value to be returned if the optional expression is not found.
+ Example:
- Example::
+ .. testcode::
# US postal code can be a 5-digit zip, plus optional 4-digit qualifier
zip = Combine(Word(nums, exact=5) + Opt('-' + Word(nums, exact=4)))
@@ -5334,7 +5706,11 @@ class Opt(ParseElementEnhance):
98765-
''')
- prints::
+ prints:
+
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
+
# traditional ZIP code
12345
@@ -5346,8 +5722,10 @@ class Opt(ParseElementEnhance):
# invalid ZIP
98765-
+ 98765-
^
- FAIL: Expected end of text (at char 5), (line:1, col:6)
+ ParseException: Expected end of text, found '-' (at char 5), (line:1, col:6)
+ FAIL: Expected end of text, found '-' (at char 5), (line:1, col:6)
"""
__optionalNotMatched = _NullToken()
@@ -5394,19 +5772,23 @@ class SkipTo(ParseElementEnhance):
Token for skipping over all undefined text until the matched
expression is found.
- Parameters:
+ :param expr: target expression marking the end of the data to be skipped
+ :param include: if ``True``, the target expression is also parsed
+ (the skipped text and target expression are returned
+ as a 2-element list) (default= ``False``).
+
+ :param ignore: (default= ``None``) used to define grammars
+ (typically quoted strings and comments)
+ that might contain false matches to the target expression
- - ``expr`` - target expression marking the end of the data to be skipped
- - ``include`` - if ``True``, the target expression is also parsed
- (the skipped text and target expression are returned as a 2-element
- list) (default= ``False``).
- - ``ignore`` - (default= ``None``) used to define grammars (typically quoted strings and
- comments) that might contain false matches to the target expression
- - ``fail_on`` - (default= ``None``) define expressions that are not allowed to be
- included in the skipped test; if found before the target expression is found,
- the :class:`SkipTo` is not a match
+ :param fail_on: (default= ``None``) define expressions that
+ are not allowed to be included in the skipped test;
+ if found before the target expression is found,
+ the :class:`SkipTo` is not a match
- Example::
+ Example:
+
+ .. testcode::
report = '''
Outstanding Issues Report - 1 Jan 2000
@@ -5430,9 +5812,11 @@ class SkipTo(ParseElementEnhance):
+ integer("days_open"))
for tkt in ticket_expr.search_string(report):
- print tkt.dump()
+ print(tkt.dump())
- prints::
+ prints:
+
+ .. testoutput::
['101', 'Critical', 'Intermittent system crash', '6']
- days_open: '6'
@@ -5546,28 +5930,31 @@ class Forward(ParseElementEnhance):
Forward declaration of an expression to be defined later -
used for recursive grammars, such as algebraic infix notation.
When the expression is known, it is assigned to the ``Forward``
- variable using the ``'<<'`` operator.
+ instance using the ``'<<'`` operator.
+
+ .. Note::
- Note: take care when assigning to ``Forward`` not to overlook
- precedence of operators.
+ Take care when assigning to ``Forward`` not to overlook
+ precedence of operators.
- Specifically, ``'|'`` has a lower precedence than ``'<<'``, so that::
+ Specifically, ``'|'`` has a lower precedence than ``'<<'``, so that::
- fwd_expr << a | b | c
+ fwd_expr << a | b | c
- will actually be evaluated as::
+ will actually be evaluated as::
- (fwd_expr << a) | b | c
+ (fwd_expr << a) | b | c
- thereby leaving b and c out as parseable alternatives. It is recommended that you
- explicitly group the values inserted into the ``Forward``::
+ thereby leaving b and c out as parseable alternatives.
+ It is recommended that you explicitly group the values
+ inserted into the :class:`Forward`::
- fwd_expr << (a | b | c)
+ fwd_expr << (a | b | c)
- Converting to use the ``'<<='`` operator instead will avoid this problem.
+ Converting to use the ``'<<='`` operator instead will avoid this problem.
- See :class:`ParseResults.pprint` for an example of a recursive
- parser created using ``Forward``.
+ See :meth:`ParseResults.pprint` for an example of a recursive
+ parser created using :class:`Forward`.
"""
def __init__(
@@ -5826,17 +6213,26 @@ class Combine(TokenConverter):
input string; this can be disabled by specifying
``'adjacent=False'`` in the constructor.
- Example::
+ Example:
+
+ .. doctest::
+
+ >>> real = Word(nums) + '.' + Word(nums)
+ >>> print(real.parse_string('3.1416'))
+ ['3', '.', '1416']
+
+ >>> # will also erroneously match the following
+ >>> print(real.parse_string('3. 1416'))
+ ['3', '.', '1416']
- real = Word(nums) + '.' + Word(nums)
- print(real.parse_string('3.1416')) # -> ['3', '.', '1416']
- # will also erroneously match the following
- print(real.parse_string('3. 1416')) # -> ['3', '.', '1416']
+ >>> real = Combine(Word(nums) + '.' + Word(nums))
+ >>> print(real.parse_string('3.1416'))
+ ['3.1416']
- real = Combine(Word(nums) + '.' + Word(nums))
- print(real.parse_string('3.1416')) # -> ['3.1416']
- # no match when there are internal spaces
- print(real.parse_string('3. 1416')) # -> Exception: Expected W:(0123...)
+ >>> # no match when there are internal spaces
+ >>> print(real.parse_string('3. 1416'))
+ Traceback (most recent call last):
+ ParseException: Expected W:(0123...)
"""
def __init__(
@@ -5884,18 +6280,20 @@ class Group(TokenConverter):
The optional ``aslist`` argument when set to True will return the
parsed tokens as a Python list instead of a pyparsing ParseResults.
- Example::
+ Example:
+
+ .. doctest::
- ident = Word(alphas)
- num = Word(nums)
- term = ident | num
- func = ident + Opt(DelimitedList(term))
- print(func.parse_string("fn a, b, 100"))
- # -> ['fn', 'a', 'b', '100']
+ >>> ident = Word(alphas)
+ >>> num = Word(nums)
+ >>> term = ident | num
+ >>> func = ident + Opt(DelimitedList(term))
+ >>> print(func.parse_string("fn a, b, 100"))
+ ['fn', 'a', 'b', '100']
- func = ident + Group(Opt(DelimitedList(term)))
- print(func.parse_string("fn a, b, 100"))
- # -> ['fn', ['a', 'b', '100']]
+ >>> func = ident + Group(Opt(DelimitedList(term)))
+ >>> print(func.parse_string("fn a, b, 100"))
+ ['fn', ['a', 'b', '100']]
"""
def __init__(self, expr: ParserElement, aslist: bool = False) -> None:
@@ -5923,35 +6321,48 @@ class Dict(TokenConverter):
The optional ``asdict`` argument when set to True will return the
parsed tokens as a Python dict instead of a pyparsing ParseResults.
- Example::
+ Example:
- data_word = Word(alphas)
- label = data_word + FollowedBy(':')
+ .. doctest::
- text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
- attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join))
+ >>> data_word = Word(alphas)
+ >>> label = data_word + FollowedBy(':')
- # print attributes as plain groups
- print(attr_expr[1, ...].parse_string(text).dump())
+ >>> attr_expr = (
+ ... label + Suppress(':')
+ ... + OneOrMore(data_word, stop_on=label)
+ ... .set_parse_action(' '.join)
+ ... )
- # instead of OneOrMore(expr), parse using Dict(Group(expr)[1, ...]) - Dict will auto-assign names
- result = Dict(Group(attr_expr)[1, ...]).parse_string(text)
- print(result.dump())
-
- # access named fields as dict entries, or output as dict
- print(result['shape'])
- print(result.as_dict())
-
- prints::
+ >>> text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+ >>> # print attributes as plain groups
+ >>> print(attr_expr[1, ...].parse_string(text).dump())
['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
+
+ # instead of OneOrMore(expr), parse using Dict(Group(expr)[1, ...])
+ # Dict will auto-assign names.
+ >>> result = Dict(Group(attr_expr)[1, ...]).parse_string(text)
+ >>> print(result.dump())
[['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
- color: 'light blue'
- posn: 'upper left'
- shape: 'SQUARE'
- texture: 'burlap'
+ [0]:
+ ['shape', 'SQUARE']
+ [1]:
+ ['posn', 'upper left']
+ [2]:
+ ['color', 'light blue']
+ [3]:
+ ['texture', 'burlap']
+
+ # access named fields as dict entries, or output as dict
+ >>> print(result['shape'])
SQUARE
- {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
+ >>> print(result.as_dict())
+ {'shape': 'SQUARE', 'posn': 'upper left', 'color': 'light blue', 'texture': 'burlap'}
See more examples at :class:`ParseResults` of accessing fields by results name.
"""
@@ -6004,29 +6415,28 @@ class Dict(TokenConverter):
class Suppress(TokenConverter):
"""Converter for ignoring the results of a parsed expression.
- Example::
+ Example:
- source = "a, b, c,d"
- wd = Word(alphas)
- wd_list1 = wd + (',' + wd)[...]
- print(wd_list1.parse_string(source))
+ .. doctest::
+
+ >>> source = "a, b, c,d"
+ >>> wd = Word(alphas)
+ >>> wd_list1 = wd + (',' + wd)[...]
+ >>> print(wd_list1.parse_string(source))
+ ['a', ',', 'b', ',', 'c', ',', 'd']
# often, delimiters that are useful during parsing are just in the
# way afterward - use Suppress to keep them out of the parsed output
- wd_list2 = wd + (Suppress(',') + wd)[...]
- print(wd_list2.parse_string(source))
+ >>> wd_list2 = wd + (Suppress(',') + wd)[...]
+ >>> print(wd_list2.parse_string(source))
+ ['a', 'b', 'c', 'd']
# Skipped text (using '...') can be suppressed as well
- source = "lead in START relevant text END trailing text"
- start_marker = Keyword("START")
- end_marker = Keyword("END")
- find_body = Suppress(...) + start_marker + ... + end_marker
- print(find_body.parse_string(source)
-
- prints::
-
- ['a', ',', 'b', ',', 'c', ',', 'd']
- ['a', 'b', 'c', 'd']
+ >>> source = "lead in START relevant text END trailing text"
+ >>> start_marker = Keyword("START")
+ >>> end_marker = Keyword("END")
+ >>> find_body = Suppress(...) + start_marker + ... + end_marker
+ >>> print(find_body.parse_string(source))
['START', 'relevant text ', 'END']
(See also :class:`DelimitedList`.)
@@ -6056,6 +6466,7 @@ class Suppress(TokenConverter):
return self
+# XXX: Example needs to be re-done for updated output
def trace_parse_action(f: ParseAction) -> ParseAction:
"""Decorator for debugging parse actions.
@@ -6064,7 +6475,18 @@ def trace_parse_action(f: ParseAction) -> ParseAction:
When the parse action completes, the decorator will print
``"<<"`` followed by the returned value, or any exception that the parse action raised.
- Example::
+ Example:
+
+ .. testsetup:: stderr
+
+ import sys
+ sys.stderr = sys.stdout
+
+ .. testcleanup:: stderr
+
+ sys.stderr = sys.__stderr__
+
+ .. testcode:: stderr
wd = Word(alphas)
@@ -6075,11 +6497,18 @@ def trace_parse_action(f: ParseAction) -> ParseAction:
wds = wd[1, ...].set_parse_action(remove_duplicate_chars)
print(wds.parse_string("slkdjs sld sldd sdlf sdljf"))
- prints::
+ prints:
- >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
+ .. testoutput:: stderr
+ :options: +NORMALIZE_WHITESPACE
+
+ >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf',
+ 0, ParseResults(['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
<<leaving remove_duplicate_chars (ret: 'dfjkls')
['dfjkls']
+
+ .. versionchanged:: 3.1.0
+ Exception type added to output
"""
f = _trim_arity(f)
@@ -6255,6 +6684,8 @@ quoted_string = Combine(
)
).set_name("quoted string using single or double quotes")
+# XXX: Is there some way to make this show up in API docs?
+# .. versionadded:: 3.1.0
python_quoted_string = Combine(
(Regex(r'"""(?:[^"\\]|""(?!")|"(?!"")|\\.)*', flags=re.MULTILINE) + '"""').set_name(
"multiline double quoted string"
diff --git a/contrib/python/pyparsing/py3/pyparsing/diagram/__init__.py b/contrib/python/pyparsing/py3/pyparsing/diagram/__init__.py
index 526cf3862a4..af1aa47bbca 100644
--- a/contrib/python/pyparsing/py3/pyparsing/diagram/__init__.py
+++ b/contrib/python/pyparsing/py3/pyparsing/diagram/__init__.py
@@ -112,9 +112,14 @@ T = TypeVar("T")
class EachItem(railroad.Group):
"""
Custom railroad item to compose a:
- - Group containing a
- - OneOrMore containing a
- - Choice of the elements in the Each
+
+ - :class:`railroad.Group` containing a
+
+ - :class:`railroad.OneOrMore` containing a
+
+ - :class:`railroad.Choice` of the elements in the
+ :class:`railroad.Each`
+
with the group label indicating that all must be matched
"""
@@ -152,8 +157,9 @@ class EditablePartial(Generic[T]):
@classmethod
def from_call(cls, func: Callable[..., T], *args, **kwargs) -> EditablePartial[T]:
"""
- If you call this function in the same way that you would call the constructor, it will store the arguments
- as you expect. For example EditablePartial.from_call(Fraction, 1, 3)() == Fraction(1, 3)
+ If you call this function in the same way that you would call the constructor,
+ it will store the arguments as you expect. For example
+ ``EditablePartial.from_call(Fraction, 1, 3)() == Fraction(1, 3)``
"""
return EditablePartial(func=func, args=list(args), kwargs=kwargs)
@@ -179,7 +185,9 @@ class EditablePartial(Generic[T]):
def railroad_to_html(diagrams: list[NamedDiagram], embed=False, **kwargs) -> str:
"""
- Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams
+ Given a list of :class:`NamedDiagram`, produce a single HTML string
+ that visualises those diagrams.
+
:params kwargs: kwargs to be passed in to the template
"""
data = []
@@ -231,16 +239,22 @@ def to_railroad(
"""
Convert a pyparsing element tree into a list of diagrams. This is the recommended entrypoint to diagram
creation if you want to access the Railroad tree before it is converted to HTML
+
:param element: base element of the parser being diagrammed
- :param diagram_kwargs: kwargs to pass to the Diagram() constructor
- :param vertical: (optional) - int - limit at which number of alternatives should be
- shown vertically instead of horizontally
- :param show_results_names - bool to indicate whether results name annotations should be
- included in the diagram
- :param show_groups - bool to indicate whether groups should be highlighted with an unlabeled
- surrounding box
- :param show_hidden - bool to indicate whether internal elements that are typically hidden
- should be shown
+
+ :param diagram_kwargs: kwargs to pass to the :meth:`Diagram` constructor
+
+ :param vertical: (optional) int - limit at which number of alternatives
+ should be shown vertically instead of horizontally
+
+ :param show_results_names: bool to indicate whether results name
+ annotations should be included in the diagram
+
+ :param show_groups: bool to indicate whether groups should be highlighted
+ with an unlabeled surrounding box
+
+ :param show_hidden: bool to indicate whether internal elements that are
+ typically hidden should be shown
"""
# Convert the whole tree underneath the root
lookup = ConverterState(diagram_kwargs=diagram_kwargs or {})
diff --git a/contrib/python/pyparsing/py3/pyparsing/exceptions.py b/contrib/python/pyparsing/py3/pyparsing/exceptions.py
index fe07a855856..2c62ee357d6 100644
--- a/contrib/python/pyparsing/py3/pyparsing/exceptions.py
+++ b/contrib/python/pyparsing/py3/pyparsing/exceptions.py
@@ -192,10 +192,20 @@ class ParseBaseException(Exception):
return copy.copy(self)
def formatted_message(self) -> str:
+ """
+ Output the formatted exception message.
+ Can be overridden to customize the message formatting or contents.
+
+ .. versionadded:: 3.2.0
+ """
found_phrase = f", found {self.found}" if self.found else ""
return f"{self.msg}{found_phrase} (at char {self.loc}), (line:{self.lineno}, col:{self.column})"
def __str__(self) -> str:
+ """
+ .. versionchanged:: 3.2.0
+ Now uses :meth:`formatted_message` to format message.
+ """
return self.formatted_message()
def __repr__(self):
@@ -229,7 +239,9 @@ class ParseBaseException(Exception):
Returns a multi-line string listing the ParserElements and/or function names in the
exception's stack trace.
- Example::
+ Example:
+
+ .. testcode::
# an expression to parse 3 integers
expr = pp.Word(pp.nums) * 3
@@ -239,11 +251,13 @@ class ParseBaseException(Exception):
except pp.ParseException as pe:
print(pe.explain(depth=0))
- prints::
+ prints:
+
+ .. testoutput::
123 456 A789
^
- ParseException: Expected W:(0-9), found 'A' (at char 8), (line:1, col:9)
+ ParseException: Expected W:(0-9), found 'A789' (at char 8), (line:1, col:9)
Note: the diagnostic output will include string representations of the expressions
that failed to parse. These representations will be more helpful if you use `set_name` to
@@ -266,7 +280,9 @@ class ParseException(ParseBaseException):
"""
Exception thrown when a parse expression doesn't match the input string
- Example::
+ Example:
+
+ .. testcode::
integer = Word(nums).set_name("integer")
try:
@@ -274,7 +290,9 @@ class ParseException(ParseBaseException):
except ParseException as pe:
print(pe, f"column: {pe.column}")
- prints::
+ prints:
+
+ .. testoutput::
Expected integer, found 'ABC' (at char 0), (line:1, col:1) column: 1
@@ -299,11 +317,12 @@ class ParseSyntaxException(ParseFatalException):
class RecursiveGrammarException(Exception):
"""
+ .. deprecated:: 3.0.0
+ Only used by the deprecated :meth:`ParserElement.validate`.
+
Exception thrown by :class:`ParserElement.validate` if the
grammar could be left-recursive; parser may need to enable
left recursion using :class:`ParserElement.enable_left_recursion<ParserElement.enable_left_recursion>`
-
- Deprecated: only used by deprecated method ParserElement.validate.
"""
def __init__(self, parseElementList) -> None:
diff --git a/contrib/python/pyparsing/py3/pyparsing/helpers.py b/contrib/python/pyparsing/py3/pyparsing/helpers.py
index 2badd68d64f..09697eda156 100644
--- a/contrib/python/pyparsing/py3/pyparsing/helpers.py
+++ b/contrib/python/pyparsing/py3/pyparsing/helpers.py
@@ -38,27 +38,38 @@ def counted_array(
If ``int_expr`` is specified, it should be a pyparsing expression
that produces an integer value.
- Example::
+ Examples:
- counted_array(Word(alphas)).parse_string('2 ab cd ef') # -> ['ab', 'cd']
+ .. doctest::
- # in this parser, the leading integer value is given in binary,
- # '10' indicating that 2 values are in the array
- binary_constant = Word('01').set_parse_action(lambda t: int(t[0], 2))
- counted_array(Word(alphas), int_expr=binary_constant).parse_string('10 ab cd ef') # -> ['ab', 'cd']
+ >>> counted_array(Word(alphas)).parse_string('2 ab cd ef')
+ ParseResults(['ab', 'cd'], {})
- # if other fields must be parsed after the count but before the
- # list items, give the fields results names and they will
- # be preserved in the returned ParseResults:
- count_with_metadata = integer + Word(alphas)("type")
- typed_array = counted_array(Word(alphanums), int_expr=count_with_metadata)("items")
- result = typed_array.parse_string("3 bool True True False")
- print(result.dump())
+ - In this parser, the leading integer value is given in binary,
+ '10' indicating that 2 values are in the array:
- # prints
- # ['True', 'True', 'False']
- # - items: ['True', 'True', 'False']
- # - type: 'bool'
+ .. doctest::
+
+ >>> binary_constant = Word('01').set_parse_action(lambda t: int(t[0], 2))
+ >>> counted_array(Word(alphas), int_expr=binary_constant
+ ... ).parse_string('10 ab cd ef')
+ ParseResults(['ab', 'cd'], {})
+
+ - If other fields must be parsed after the count but before the
+ list items, give the fields results names and they will
+ be preserved in the returned ParseResults:
+
+ .. doctest::
+
+ >>> ppc = pyparsing.common
+ >>> count_with_metadata = ppc.integer + Word(alphas)("type")
+ >>> typed_array = counted_array(Word(alphanums),
+ ... int_expr=count_with_metadata)("items")
+ >>> result = typed_array.parse_string("3 bool True True False")
+ >>> print(result.dump())
+ ['True', 'True', 'False']
+ - items: ['True', 'True', 'False']
+ - type: 'bool'
"""
intExpr = intExpr or int_expr
array_expr = Forward()
@@ -84,9 +95,11 @@ def match_previous_literal(expr: ParserElement) -> ParserElement:
the tokens matched in a previous expression, that is, it looks for
a 'repeat' of a previous expression. For example::
- first = Word(nums)
- second = match_previous_literal(first)
- match_expr = first + ":" + second
+ .. testcode::
+
+ first = Word(nums)
+ second = match_previous_literal(first)
+ match_expr = first + ":" + second
will match ``"1:1"``, but not ``"1:2"``. Because this
matches a previous literal, will also match the leading
@@ -117,11 +130,13 @@ def match_previous_literal(expr: ParserElement) -> ParserElement:
def match_previous_expr(expr: ParserElement) -> ParserElement:
"""Helper to define an expression that is indirectly defined from
the tokens matched in a previous expression, that is, it looks for
- a 'repeat' of a previous expression. For example::
+ a 'repeat' of a previous expression. For example:
+
+ .. testcode::
- first = Word(nums)
- second = match_previous_expr(first)
- match_expr = first + ":" + second
+ first = Word(nums)
+ second = match_previous_expr(first)
+ match_expr = first + ":" + second
will match ``"1:1"``, but not ``"1:2"``. Because this
matches by expressions, will *not* match the leading ``"1:1"``
@@ -164,32 +179,35 @@ def one_of(
regardless of the input order, but returns
a :class:`MatchFirst` for best performance.
- Parameters:
+ :param strs: a string of space-delimited literals, or a collection of
+ string literals
+ :param caseless: treat all literals as caseless
+ :param use_regex: bool - as an optimization, will
+ generate a :class:`Regex` object; otherwise, will generate
+ a :class:`MatchFirst` object (if ``caseless=True`` or
+ ``as_keyword=True``, or if creating a :class:`Regex` raises an exception)
+ :param as_keyword: bool - enforce :class:`Keyword`-style matching on the
+ generated expressions
+
+ Parameters ``asKeyword`` and ``useRegex`` are retained for pre-PEP8
+ compatibility, but will be removed in a future release.
+
+ Example:
- - ``strs`` - a string of space-delimited literals, or a collection of
- string literals
- - ``caseless`` - treat all literals as caseless - (default= ``False``)
- - ``use_regex`` - as an optimization, will
- generate a :class:`Regex` object; otherwise, will generate
- a :class:`MatchFirst` object (if ``caseless=True`` or ``as_keyword=True``, or if
- creating a :class:`Regex` raises an exception) - (default= ``True``)
- - ``as_keyword`` - enforce :class:`Keyword`-style matching on the
- generated expressions - (default= ``False``)
- - ``asKeyword`` and ``useRegex`` are retained for pre-PEP8 compatibility,
- but will be removed in a future release
+ .. testcode::
- Example::
+ comp_oper = one_of("< = > <= >= !=")
+ var = Word(alphas)
+ number = Word(nums)
+ term = var | number
+ comparison_expr = term + comp_oper + term
+ print(comparison_expr.search_string("B = 12 AA=23 B<=AA AA>12"))
- comp_oper = one_of("< = > <= >= !=")
- var = Word(alphas)
- number = Word(nums)
- term = var | number
- comparison_expr = term + comp_oper + term
- print(comparison_expr.search_string("B = 12 AA=23 B<=AA AA>12"))
+ prints:
- prints::
+ .. testoutput::
- [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
+ [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
"""
asKeyword = asKeyword or as_keyword
useRegex = useRegex and use_regex
@@ -254,7 +272,7 @@ def one_of(
patt = rf"\b(?:{patt})\b"
ret = Regex(patt, flags=re_flags)
- ret.set_name(" | ".join(re.escape(s) for s in symbols))
+ ret.set_name(" | ".join(repr(s) for s in symbols))
if caseless:
# add parse action to return symbols as specified, not in random
@@ -293,32 +311,49 @@ def dict_of(key: ParserElement, value: ParserElement) -> Dict:
pattern can include named results, so that the :class:`Dict` results
can include named token fields.
- Example::
+ Example:
- text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
- attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join))
- print(attr_expr[1, ...].parse_string(text).dump())
+ .. doctest::
- attr_label = label
- attr_value = Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)
+ >>> text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+
+ >>> data_word = Word(alphas)
+ >>> label = data_word + FollowedBy(':')
+ >>> attr_expr = (
+ ... label
+ ... + Suppress(':')
+ ... + OneOrMore(data_word, stop_on=label)
+ ... .set_parse_action(' '.join))
+ >>> print(attr_expr[1, ...].parse_string(text).dump())
+ ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
- # similar to Dict, but simpler call format
- result = dict_of(attr_label, attr_value).parse_string(text)
- print(result.dump())
- print(result['shape'])
- print(result.shape) # object attribute access works too
- print(result.as_dict())
+ >>> attr_label = label
+ >>> attr_value = Suppress(':') + OneOrMore(data_word, stop_on=label
+ ... ).set_parse_action(' '.join)
- prints::
+ # similar to Dict, but simpler call format
+ >>> result = dict_of(attr_label, attr_value).parse_string(text)
+ >>> print(result.dump())
+ [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+ - color: 'light blue'
+ - posn: 'upper left'
+ - shape: 'SQUARE'
+ - texture: 'burlap'
+ [0]:
+ ['shape', 'SQUARE']
+ [1]:
+ ['posn', 'upper left']
+ [2]:
+ ['color', 'light blue']
+ [3]:
+ ['texture', 'burlap']
- [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
- - color: 'light blue'
- - posn: 'upper left'
- - shape: 'SQUARE'
- - texture: 'burlap'
- SQUARE
- SQUARE
- {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
+ >>> print(result['shape'])
+ SQUARE
+ >>> print(result.shape) # object attribute access works too
+ SQUARE
+ >>> print(result.as_dict())
+ {'shape': 'SQUARE', 'posn': 'upper left', 'color': 'light blue', 'texture': 'burlap'}
"""
return Dict(OneOrMore(Group(key + value)))
@@ -344,18 +379,22 @@ def original_text_for(
The ``asString`` pre-PEP8 argument is retained for compatibility,
but will be removed in a future release.
- Example::
+ Example:
- src = "this is test <b> bold <i>text</i> </b> normal text "
- for tag in ("b", "i"):
- opener, closer = make_html_tags(tag)
- patt = original_text_for(opener + ... + closer)
- print(patt.search_string(src)[0])
+ .. testcode::
- prints::
+ src = "this is test <b> bold <i>text</i> </b> normal text "
+ for tag in ("b", "i"):
+ opener, closer = make_html_tags(tag)
+ patt = original_text_for(opener + ... + closer)
+ print(patt.search_string(src)[0])
- ['<b> bold <i>text</i> </b>']
- ['<i>text</i>']
+ prints:
+
+ .. testoutput::
+
+ ['<b> bold <i>text</i> </b>']
+ ['<i>text</i>']
"""
asString = asString and as_string
@@ -385,7 +424,9 @@ def ungroup(expr: ParserElement) -> ParserElement:
def locatedExpr(expr: ParserElement) -> ParserElement:
"""
- (DEPRECATED - future code should use the :class:`Located` class)
+ .. deprecated:: 3.0.0
+ Use the :class:`Located` class instead.
+
Helper to decorate a returned token with its starting and ending
locations in the input string.
@@ -396,19 +437,24 @@ def locatedExpr(expr: ParserElement) -> ParserElement:
- ``value`` - the actual parsed results
Be careful if the input text contains ``<TAB>`` characters, you
- may want to call :class:`ParserElement.parse_with_tabs`
+ may want to call :meth:`ParserElement.parse_with_tabs`
+
+ Example:
+
+ .. testcode::
- Example::
+ wd = Word(alphas)
+ res = locatedExpr(wd).search_string("ljsdf123lksdjjf123lkkjj1222")
+ for match in res:
+ print(match)
- wd = Word(alphas)
- for match in locatedExpr(wd).search_string("ljsdf123lksdjjf123lkkjj1222"):
- print(match)
+ prints:
- prints::
+ .. testoutput::
- [[0, 'ljsdf', 5]]
- [[8, 'lksdjjf', 15]]
- [[18, 'lkkjj', 23]]
+ [[0, 'ljsdf', 5]]
+ [[8, 'lksdjjf', 15]]
+ [[18, 'lkkjj', 23]]
"""
locator = Empty().set_parse_action(lambda ss, ll, tt: ll)
return Group(
@@ -427,25 +473,26 @@ def nested_expr(
opener: Union[str, ParserElement] = "(",
closer: Union[str, ParserElement] = ")",
content: typing.Optional[ParserElement] = None,
- ignore_expr: ParserElement = _NO_IGNORE_EXPR_GIVEN,
+ ignore_expr: typing.Optional[ParserElement] = _NO_IGNORE_EXPR_GIVEN,
*,
- ignoreExpr: ParserElement = _NO_IGNORE_EXPR_GIVEN,
+ ignoreExpr: typing.Optional[ParserElement] = _NO_IGNORE_EXPR_GIVEN,
) -> ParserElement:
"""Helper method for defining nested lists enclosed in opening and
closing delimiters (``"("`` and ``")"`` are the default).
- Parameters:
+ :param opener: str - opening character for a nested list
+ (default= ``"("``); can also be a pyparsing expression
+
+ :param closer: str - closing character for a nested list
+ (default= ``")"``); can also be a pyparsing expression
- - ``opener`` - opening character for a nested list
- (default= ``"("``); can also be a pyparsing expression
- - ``closer`` - closing character for a nested list
- (default= ``")"``); can also be a pyparsing expression
- - ``content`` - expression for items within the nested lists
- (default= ``None``)
- - ``ignore_expr`` - expression for ignoring opening and closing delimiters
- (default= :class:`quoted_string`)
- - ``ignoreExpr`` - this pre-PEP8 argument is retained for compatibility
- but will be removed in a future release
+ :param content: expression for items within the nested lists
+
+ :param ignore_expr: expression for ignoring opening and closing delimiters
+ (default = :class:`quoted_string`)
+
+ Parameter ``ignoreExpr`` is retained for compatibility
+ but will be removed in a future release.
If an expression is not provided for the content argument, the
nested expression will capture all whitespace-delimited content
@@ -459,44 +506,48 @@ def nested_expr(
:class:`quoted_string`, but if no expressions are to be ignored, then
pass ``None`` for this argument.
- Example::
+ Example:
+
+ .. testcode::
+
+ data_type = one_of("void int short long char float double")
+ decl_data_type = Combine(data_type + Opt(Word('*')))
+ ident = Word(alphas+'_', alphanums+'_')
+ number = pyparsing_common.number
+ arg = Group(decl_data_type + ident)
+ LPAR, RPAR = map(Suppress, "()")
- data_type = one_of("void int short long char float double")
- decl_data_type = Combine(data_type + Opt(Word('*')))
- ident = Word(alphas+'_', alphanums+'_')
- number = pyparsing_common.number
- arg = Group(decl_data_type + ident)
- LPAR, RPAR = map(Suppress, "()")
+ code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment))
- code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment))
+ c_function = (decl_data_type("type")
+ + ident("name")
+ + LPAR + Opt(DelimitedList(arg), [])("args") + RPAR
+ + code_body("body"))
+ c_function.ignore(c_style_comment)
- c_function = (decl_data_type("type")
- + ident("name")
- + LPAR + Opt(DelimitedList(arg), [])("args") + RPAR
- + code_body("body"))
- c_function.ignore(c_style_comment)
+ source_code = '''
+ int is_odd(int x) {
+ return (x%2);
+ }
- source_code = '''
- int is_odd(int x) {
- return (x%2);
- }
+ int dec_to_hex(char hchar) {
+ if (hchar >= '0' && hchar <= '9') {
+ return (ord(hchar)-ord('0'));
+ } else {
+ return (10+ord(hchar)-ord('A'));
+ }
+ }
+ '''
+ for func in c_function.search_string(source_code):
+ print(f"{func.name} ({func.type}) args: {func.args}")
- int dec_to_hex(char hchar) {
- if (hchar >= '0' && hchar <= '9') {
- return (ord(hchar)-ord('0'));
- } else {
- return (10+ord(hchar)-ord('A'));
- }
- }
- '''
- for func in c_function.search_string(source_code):
- print("%(name)s (%(type)s) args: %(args)s" % func)
+ prints:
- prints::
+ .. testoutput::
- is_odd (int) args: [['int', 'x']]
- dec_to_hex (int) args: [['char', 'hchar']]
+ is_odd (int) args: [['int', 'x']]
+ dec_to_hex (int) args: [['char', 'hchar']]
"""
if ignoreExpr != ignore_expr:
ignoreExpr = ignore_expr if ignoreExpr is _NO_IGNORE_EXPR_GIVEN else ignoreExpr
@@ -574,7 +625,8 @@ def nested_expr(
def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")):
- """Internal helper to construct opening and closing tag expressions, given a tag name"""
+ """Internal helper to construct opening and closing tag expressions,
+ given a tag name"""
if isinstance(tagStr, str_type):
resname = tagStr
tagStr = Keyword(tagStr, caseless=not xml)
@@ -638,22 +690,26 @@ def make_html_tags(
given a tag name. Matches tags in either upper or lower case,
attributes with namespaces and with quoted or unquoted values.
- Example::
+ Example:
+
+ .. testcode::
+
+ text = '<td>More info at the <a href="https://github.com/pyparsing/pyparsing/wiki">pyparsing</a> wiki page</td>'
+ # make_html_tags returns pyparsing expressions for the opening and
+ # closing tags as a 2-tuple
+ a, a_end = make_html_tags("A")
+ link_expr = a + SkipTo(a_end)("link_text") + a_end
- text = '<td>More info at the <a href="https://github.com/pyparsing/pyparsing/wiki">pyparsing</a> wiki page</td>'
- # make_html_tags returns pyparsing expressions for the opening and
- # closing tags as a 2-tuple
- a, a_end = make_html_tags("A")
- link_expr = a + SkipTo(a_end)("link_text") + a_end
+ for link in link_expr.search_string(text):
+ # attributes in the <A> tag (like "href" shown here) are
+ # also accessible as named results
+ print(link.link_text, '->', link.href)
- for link in link_expr.search_string(text):
- # attributes in the <A> tag (like "href" shown here) are
- # also accessible as named results
- print(link.link_text, '->', link.href)
+ prints:
- prints::
+ .. testoutput::
- pyparsing -> https://github.com/pyparsing/pyparsing/wiki
+ pyparsing -> https://github.com/pyparsing/pyparsing/wiki
"""
return _makeTags(tag_str, False)
@@ -735,69 +791,77 @@ def infix_notation(
Parameters:
- - ``base_expr`` - expression representing the most basic operand to
- be used in the expression
- - ``op_list`` - list of tuples, one for each operator precedence level
- in the expression grammar; each tuple is of the form ``(op_expr,
- num_operands, right_left_assoc, (optional)parse_action)``, where:
+ :param base_expr: expression representing the most basic operand to
+ be used in the expression
+ :param op_list: list of tuples, one for each operator precedence level
+ in the expression grammar; each tuple is of the form ``(op_expr,
+ num_operands, right_left_assoc, (optional)parse_action)``, where:
+
+ - ``op_expr`` is the pyparsing expression for the operator; may also
+ be a string, which will be converted to a Literal; if ``num_operands``
+ is 3, ``op_expr`` is a tuple of two expressions, for the two
+ operators separating the 3 terms
+ - ``num_operands`` is the number of terms for this operator (must be 1,
+ 2, or 3)
+ - ``right_left_assoc`` is the indicator whether the operator is right
+ or left associative, using the pyparsing-defined constants
+ ``OpAssoc.RIGHT`` and ``OpAssoc.LEFT``.
+ - ``parse_action`` is the parse action to be associated with
+ expressions matching this operator expression (the parse action
+ tuple member may be omitted); if the parse action is passed
+ a tuple or list of functions, this is equivalent to calling
+ ``set_parse_action(*fn)``
+ (:class:`ParserElement.set_parse_action`)
+
+ :param lpar: expression for matching left-parentheses; if passed as a
+ str, then will be parsed as ``Suppress(lpar)``. If lpar is passed as
+ an expression (such as ``Literal('(')``), then it will be kept in
+ the parsed results, and grouped with them. (default= ``Suppress('(')``)
+ :param rpar: expression for matching right-parentheses; if passed as a
+ str, then will be parsed as ``Suppress(rpar)``. If rpar is passed as
+ an expression (such as ``Literal(')')``), then it will be kept in
+ the parsed results, and grouped with them. (default= ``Suppress(')')``)
+
+ Example:
+
+ .. testcode::
- - ``op_expr`` is the pyparsing expression for the operator; may also
- be a string, which will be converted to a Literal; if ``num_operands``
- is 3, ``op_expr`` is a tuple of two expressions, for the two
- operators separating the 3 terms
- - ``num_operands`` is the number of terms for this operator (must be 1,
- 2, or 3)
- - ``right_left_assoc`` is the indicator whether the operator is right
- or left associative, using the pyparsing-defined constants
- ``OpAssoc.RIGHT`` and ``OpAssoc.LEFT``.
- - ``parse_action`` is the parse action to be associated with
- expressions matching this operator expression (the parse action
- tuple member may be omitted); if the parse action is passed
- a tuple or list of functions, this is equivalent to calling
- ``set_parse_action(*fn)``
- (:class:`ParserElement.set_parse_action`)
- - ``lpar`` - expression for matching left-parentheses; if passed as a
- str, then will be parsed as ``Suppress(lpar)``. If lpar is passed as
- an expression (such as ``Literal('(')``), then it will be kept in
- the parsed results, and grouped with them. (default= ``Suppress('(')``)
- - ``rpar`` - expression for matching right-parentheses; if passed as a
- str, then will be parsed as ``Suppress(rpar)``. If rpar is passed as
- an expression (such as ``Literal(')')``), then it will be kept in
- the parsed results, and grouped with them. (default= ``Suppress(')')``)
+ # simple example of four-function arithmetic with ints and
+ # variable names
+ integer = pyparsing_common.signed_integer
+ varname = pyparsing_common.identifier
- Example::
+ arith_expr = infix_notation(integer | varname,
+ [
+ ('-', 1, OpAssoc.RIGHT),
+ (one_of('* /'), 2, OpAssoc.LEFT),
+ (one_of('+ -'), 2, OpAssoc.LEFT),
+ ])
- # simple example of four-function arithmetic with ints and
- # variable names
- integer = pyparsing_common.signed_integer
- varname = pyparsing_common.identifier
+ arith_expr.run_tests('''
+ 5+3*6
+ (5+3)*6
+ (5+x)*y
+ -2--11
+ ''', full_dump=False)
- arith_expr = infix_notation(integer | varname,
- [
- ('-', 1, OpAssoc.RIGHT),
- (one_of('* /'), 2, OpAssoc.LEFT),
- (one_of('+ -'), 2, OpAssoc.LEFT),
- ])
+ prints:
- arith_expr.run_tests('''
- 5+3*6
- (5+3)*6
- -2--11
- ''', full_dump=False)
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
- prints::
- 5+3*6
- [[5, '+', [3, '*', 6]]]
+ 5+3*6
+ [[5, '+', [3, '*', 6]]]
- (5+3)*6
- [[[5, '+', 3], '*', 6]]
+ (5+3)*6
+ [[[5, '+', 3], '*', 6]]
- (5+x)*y
- [[[5, '+', 'x'], '*', 'y']]
+ (5+x)*y
+ [[[5, '+', 'x'], '*', 'y']]
- -2--11
- [[['-', 2], '-', ['-', 11]]]
+ -2--11
+ [[['-', 2], '-', ['-', 11]]]
"""
# captive version of FollowedBy that does not do parse actions or capture results names
@@ -815,7 +879,7 @@ def infix_notation(
if isinstance(rpar, str):
rpar = Suppress(rpar)
- nested_expr = (lpar + ret + rpar).set_name(f"nested_{base_expr.name}")
+ nested_expr = (lpar + ret + rpar).set_name(f"nested_{base_expr.name}_expression")
# if lpar and rpar are not suppressed, wrap in group
if not (isinstance(lpar, Suppress) and isinstance(rpar, Suppress)):
@@ -914,89 +978,93 @@ def infix_notation(
def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]):
"""
- (DEPRECATED - use :class:`IndentedBlock` class instead)
+ .. deprecated:: 3.0.0
+ Use the :class:`IndentedBlock` class instead.
+
Helper method for defining space-delimited indentation blocks,
such as those used to define block statements in Python source code.
- Parameters:
-
- - ``blockStatementExpr`` - expression defining syntax of statement that
+ :param blockStatementExpr: expression defining syntax of statement that
is repeated within the indented block
- - ``indentStack`` - list created by caller to manage indentation stack
+
+ :param indentStack: list created by caller to manage indentation stack
(multiple ``statementWithIndentedBlock`` expressions within a single
grammar should share a common ``indentStack``)
- - ``indent`` - boolean indicating whether block must be indented beyond
+
+ :param indent: boolean indicating whether block must be indented beyond
the current level; set to ``False`` for block of left-most statements
- (default= ``True``)
A valid block must contain at least one ``blockStatement``.
(Note that indentedBlock uses internal parse actions which make it
incompatible with packrat parsing.)
- Example::
+ Example:
+
+ .. testcode::
- data = '''
- def A(z):
- A1
- B = 100
- G = A2
- A2
- A3
- B
- def BB(a,b,c):
- BB1
- def BBA():
- bba1
- bba2
- bba3
- C
- D
- def spam(x,y):
- def eggs(z):
- pass
- '''
+ data = '''
+ def A(z):
+ A1
+ B = 100
+ G = A2
+ A2
+ A3
+ B
+ def BB(a,b,c):
+ BB1
+ def BBA():
+ bba1
+ bba2
+ bba3
+ C
+ D
+ def spam(x,y):
+ def eggs(z):
+ pass
+ '''
+ indentStack = [1]
+ stmt = Forward()
- indentStack = [1]
- stmt = Forward()
+ identifier = Word(alphas, alphanums)
+ funcDecl = ("def" + identifier + Group("(" + Opt(delimitedList(identifier)) + ")") + ":")
+ func_body = indentedBlock(stmt, indentStack)
+ funcDef = Group(funcDecl + func_body)
- identifier = Word(alphas, alphanums)
- funcDecl = ("def" + identifier + Group("(" + Opt(delimitedList(identifier)) + ")") + ":")
- func_body = indentedBlock(stmt, indentStack)
- funcDef = Group(funcDecl + func_body)
+ rvalue = Forward()
+ funcCall = Group(identifier + "(" + Opt(delimitedList(rvalue)) + ")")
+ rvalue << (funcCall | identifier | Word(nums))
+ assignment = Group(identifier + "=" + rvalue)
+ stmt << (funcDef | assignment | identifier)
- rvalue = Forward()
- funcCall = Group(identifier + "(" + Opt(delimitedList(rvalue)) + ")")
- rvalue << (funcCall | identifier | Word(nums))
- assignment = Group(identifier + "=" + rvalue)
- stmt << (funcDef | assignment | identifier)
+ module_body = stmt[1, ...]
- module_body = stmt[1, ...]
+ parseTree = module_body.parseString(data)
+ parseTree.pprint()
- parseTree = module_body.parseString(data)
- parseTree.pprint()
+ prints:
- prints::
+ .. testoutput::
- [['def',
- 'A',
- ['(', 'z', ')'],
- ':',
- [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]],
- 'B',
- ['def',
- 'BB',
- ['(', 'a', 'b', 'c', ')'],
- ':',
- [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]],
- 'C',
- 'D',
- ['def',
- 'spam',
- ['(', 'x', 'y', ')'],
- ':',
- [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]]
+ [['def',
+ 'A',
+ ['(', 'z', ')'],
+ ':',
+ [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]],
+ 'B',
+ ['def',
+ 'BB',
+ ['(', 'a', 'b', 'c', ')'],
+ ':',
+ [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]],
+ 'C',
+ 'D',
+ ['def',
+ 'spam',
+ ['(', 'x', 'y', ')'],
+ ':',
+ [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]]
"""
backup_stacks.append(indentStack[:])
@@ -1096,7 +1164,10 @@ def delimited_list(
*,
allow_trailing_delim: bool = False,
) -> ParserElement:
- """(DEPRECATED - use :class:`DelimitedList` class)"""
+ """
+ .. deprecated:: 3.1.0
+ Use the :class:`DelimitedList` class instead.
+ """
return DelimitedList(
expr, delim, combine, min, max, allow_trailing_delim=allow_trailing_delim
)
diff --git a/contrib/python/pyparsing/py3/pyparsing/results.py b/contrib/python/pyparsing/py3/pyparsing/results.py
index 956230352c8..5dabe58a90d 100644
--- a/contrib/python/pyparsing/py3/pyparsing/results.py
+++ b/contrib/python/pyparsing/py3/pyparsing/results.py
@@ -1,4 +1,5 @@
# results.py
+
from __future__ import annotations
import collections
@@ -44,42 +45,48 @@ class ParseResults:
- by list index (``results[0], results[1]``, etc.)
- by attribute (``results.<results_name>`` - see :class:`ParserElement.set_results_name`)
- Example::
+ Example:
+
+ .. testcode::
+
+ integer = Word(nums)
+ date_str = (integer.set_results_name("year") + '/'
+ + integer.set_results_name("month") + '/'
+ + integer.set_results_name("day"))
+ # equivalent form:
+ # date_str = (integer("year") + '/'
+ # + integer("month") + '/'
+ # + integer("day"))
+
+ # parse_string returns a ParseResults object
+ result = date_str.parse_string("1999/12/31")
+
+ def test(s, fn=repr):
+ print(f"{s} -> {fn(eval(s))}")
- integer = Word(nums)
- date_str = (integer.set_results_name("year") + '/'
- + integer.set_results_name("month") + '/'
- + integer.set_results_name("day"))
- # equivalent form:
- # date_str = (integer("year") + '/'
- # + integer("month") + '/'
- # + integer("day"))
+ test("list(result)")
+ test("result[0]")
+ test("result['month']")
+ test("result.day")
+ test("'month' in result")
+ test("'minutes' in result")
+ test("result.dump()", str)
- # parse_string returns a ParseResults object
- result = date_str.parse_string("1999/12/31")
+ prints:
- def test(s, fn=repr):
- print(f"{s} -> {fn(eval(s))}")
- test("list(result)")
- test("result[0]")
- test("result['month']")
- test("result.day")
- test("'month' in result")
- test("'minutes' in result")
- test("result.dump()", str)
+ .. testoutput::
- prints::
+ list(result) -> ['1999', '/', '12', '/', '31']
+ result[0] -> '1999'
+ result['month'] -> '12'
+ result.day -> '31'
+ 'month' in result -> True
+ 'minutes' in result -> False
+ result.dump() -> ['1999', '/', '12', '/', '31']
+ - day: '31'
+ - month: '12'
+ - year: '1999'
- list(result) -> ['1999', '/', '12', '/', '31']
- result[0] -> '1999'
- result['month'] -> '12'
- result.day -> '31'
- 'month' in result -> True
- 'minutes' in result -> False
- result.dump() -> ['1999', '/', '12', '/', '31']
- - day: '31'
- - month: '12'
- - year: '1999'
"""
_null_values: tuple[Any, ...] = (None, [], ())
@@ -103,38 +110,59 @@ class ParseResults:
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`::
+ as actual Python lists, instead of being converted to :class:`ParseResults`:
+
+ .. testcode::
+
+ import pyparsing as pp
+ ppc = pp.common
+
+ LBRACK, RBRACK, LPAR, RPAR = pp.Suppress.using_each("[]()")
+ element = pp.Forward()
+ item = ppc.integer
+ item_list = pp.DelimitedList(element)
+ element_list = LBRACK + item_list + RBRACK | LPAR + item_list + RPAR
+ element <<= item | element_list
+
+ # add parse action to convert from ParseResults
+ # to actual Python collection types
+ @element_list.add_parse_action
+ def as_python_list(t):
+ return pp.ParseResults.List(t.as_list())
+
+ element.run_tests('''
+ 100
+ [2,3,4]
+ [[2, 1],3,4]
+ [(2, 1),3,4]
+ (2,3,4)
+ ([2, 3], 4)
+ ''', post_parse=lambda s, r: (r[0], type(r[0]))
+ )
- LBRACK, RBRACK = map(pp.Suppress, "[]")
- element = pp.Forward()
- item = ppc.integer
- element_list = LBRACK + pp.DelimitedList(element) + RBRACK
+ prints:
- # add parse actions to convert from ParseResults to actual Python collection types
- def as_python_list(t):
- return pp.ParseResults.List(t.as_list())
- element_list.add_parse_action(as_python_list)
+ .. testoutput::
+ :options: +NORMALIZE_WHITESPACE
- element <<= item | element_list
- element.run_tests('''
- 100
- [2,3,4]
- [[2, 1],3,4]
- [(2, 1),3,4]
- (2,3,4)
- ''', post_parse=lambda s, r: (r[0], type(r[0])))
+ 100
+ (100, <class 'int'>)
- prints::
+ [2,3,4]
+ ([2, 3, 4], <class 'list'>)
- 100
- (100, <class 'int'>)
+ [[2, 1],3,4]
+ ([[2, 1], 3, 4], <class 'list'>)
- [2,3,4]
- ([2, 3, 4], <class 'list'>)
+ [(2, 1),3,4]
+ ([[2, 1], 3, 4], <class 'list'>)
- [[2, 1],3,4]
- ([[2, 1], 3, 4], <class 'list'>)
+ (2,3,4)
+ ([2, 3, 4], <class 'list'>)
+
+ ([2, 3], 4)
+ ([[2, 3], 4], <class 'list'>)
(Used internally by :class:`Group` when `aslist=True`.)
"""
@@ -301,34 +329,40 @@ class ParseResults:
names. A second default return value argument is supported, just as in
``dict.pop()``.
- Example::
-
- numlist = Word(nums)[...]
- print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321']
+ Example:
- def remove_first(tokens):
- tokens.pop(0)
- numlist.add_parse_action(remove_first)
- print(numlist.parse_string("0 123 321")) # -> ['123', '321']
+ .. doctest::
- label = Word(alphas)
- patt = label("LABEL") + Word(nums)[1, ...]
- print(patt.parse_string("AAB 123 321").dump())
+ >>> numlist = Word(nums)[...]
+ >>> print(numlist.parse_string("0 123 321"))
+ ['0', '123', '321']
- # Use pop() in a parse action to remove named result (note that corresponding value is not
- # removed from list form of results)
- def remove_LABEL(tokens):
- tokens.pop("LABEL")
- return tokens
- patt.add_parse_action(remove_LABEL)
- print(patt.parse_string("AAB 123 321").dump())
+ >>> def remove_first(tokens):
+ ... tokens.pop(0)
+ ...
+ >>> numlist.add_parse_action(remove_first)
+ [W:(0-9)]...
+ >>> print(numlist.parse_string("0 123 321"))
+ ['123', '321']
- prints::
+ >>> label = Word(alphas)
+ >>> patt = label("LABEL") + Word(nums)[1, ...]
+ >>> print(patt.parse_string("AAB 123 321").dump())
+ ['AAB', '123', '321']
+ - LABEL: 'AAB'
- ['AAB', '123', '321']
- - LABEL: 'AAB'
+ >>> # Use pop() in a parse action to remove named result
+ >>> # (note that corresponding value is not
+ >>> # removed from list form of results)
+ >>> def remove_LABEL(tokens):
+ ... tokens.pop("LABEL")
+ ... return tokens
+ ...
+ >>> patt.add_parse_action(remove_LABEL)
+ {W:(A-Za-z) {W:(0-9)}...}
+ >>> print(patt.parse_string("AAB 123 321").dump())
+ ['AAB', '123', '321']
- ['AAB', '123', '321']
"""
if not args:
args = [-1]
@@ -354,15 +388,20 @@ class ParseResults:
Similar to ``dict.get()``.
- Example::
+ Example:
+
+ .. doctest::
- integer = Word(nums)
- date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+ >>> integer = Word(nums)
+ >>> date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+ >>> result = date_str.parse_string("1999/12/31")
+ >>> result.get("year")
+ '1999'
+ >>> result.get("hour", "not specified")
+ 'not specified'
+ >>> result.get("hour")
- result = date_str.parse_string("1999/12/31")
- print(result.get("year")) # -> '1999'
- print(result.get("hour", "not specified")) # -> 'not specified'
- print(result.get("hour")) # -> None
"""
if key in self:
return self[key]
@@ -375,16 +414,24 @@ class ParseResults:
Similar to ``list.insert()``.
- Example::
+ Example:
+
+ .. doctest::
- numlist = Word(nums)[...]
- print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321']
+ >>> numlist = Word(nums)[...]
+ >>> print(numlist.parse_string("0 123 321"))
+ ['0', '123', '321']
+
+ >>> # use a parse action to insert the parse location
+ >>> # in the front of the parsed results
+ >>> def insert_locn(locn, tokens):
+ ... tokens.insert(0, locn)
+ ...
+ >>> numlist.add_parse_action(insert_locn)
+ [W:(0-9)]...
+ >>> print(numlist.parse_string("0 123 321"))
+ [0, '0', '123', '321']
- # use a parse action to insert the parse location in the front of the parsed results
- def insert_locn(locn, tokens):
- tokens.insert(0, locn)
- numlist.add_parse_action(insert_locn)
- print(numlist.parse_string("0 123 321")) # -> [0, '0', '123', '321']
"""
self._toklist.insert(index, ins_string)
# fixup indices in token dictionary
@@ -398,33 +445,50 @@ class ParseResults:
"""
Add single element to end of ``ParseResults`` list of elements.
- Example::
+ Example:
+
+ .. doctest::
- numlist = Word(nums)[...]
- print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321']
+ >>> numlist = Word(nums)[...]
+ >>> print(numlist.parse_string("0 123 321"))
+ ['0', '123', '321']
- # use a parse action to compute the sum of the parsed integers, and add it to the end
- def append_sum(tokens):
- tokens.append(sum(map(int, tokens)))
- numlist.add_parse_action(append_sum)
- print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321', 444]
+ >>> # use a parse action to compute the sum of the parsed integers,
+ >>> # and add it to the end
+ >>> def append_sum(tokens):
+ ... tokens.append(sum(map(int, tokens)))
+ ...
+ >>> numlist.add_parse_action(append_sum)
+ [W:(0-9)]...
+ >>> print(numlist.parse_string("0 123 321"))
+ ['0', '123', '321', 444]
"""
self._toklist.append(item)
def extend(self, itemseq):
"""
- Add sequence of elements to end of ``ParseResults`` list of elements.
+ Add sequence of elements to end of :class:`ParseResults` list of elements.
+
+ Example:
+
+ .. testcode::
+
+ patt = Word(alphas)[1, ...]
- Example::
+ # use a parse action to append the reverse of the matched strings,
+ # to make a palindrome
+ def make_palindrome(tokens):
+ tokens.extend(reversed([t[::-1] for t in tokens]))
+ return ''.join(tokens)
- patt = Word(alphas)[1, ...]
+ patt.add_parse_action(make_palindrome)
+ print(patt.parse_string("lskdj sdlkjf lksd"))
- # use a parse action to append the reverse of the matched strings, to make a palindrome
- def make_palindrome(tokens):
- tokens.extend(reversed([t[::-1] for t in tokens]))
- return ''.join(tokens)
- patt.add_parse_action(make_palindrome)
- print(patt.parse_string("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
+ prints:
+
+ .. testoutput::
+
+ ['lskdjsdlkjflksddsklfjkldsjdksl']
"""
if isinstance(itemseq, ParseResults):
self.__iadd__(itemseq)
@@ -510,18 +574,32 @@ class ParseResults:
def as_list(self, *, flatten: bool = False) -> list:
"""
Returns the parse results as a nested list of matching tokens, all converted to strings.
- If flatten is True, all the nesting levels in the returned list are collapsed.
+ If ``flatten`` is True, all the nesting levels in the returned list are collapsed.
+
+ Example:
- Example::
+ .. doctest::
- patt = Word(alphas)[1, ...]
- result = patt.parse_string("sldkj lsdkj sldkj")
- # even though the result prints in string-like form, it is actually a pyparsing ParseResults
- print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj']
+ >>> patt = Word(alphas)[1, ...]
+ >>> result = patt.parse_string("sldkj lsdkj sldkj")
+ >>> # even though the result prints in string-like form,
+ >>> # it is actually a pyparsing ParseResults
+ >>> type(result)
+ <class 'pyparsing.results.ParseResults'>
+ >>> print(result)
+ ['sldkj', 'lsdkj', 'sldkj']
- # Use as_list() to create an actual list
- result_list = result.as_list()
- print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj']
+ .. doctest::
+
+ >>> # Use as_list() to create an actual list
+ >>> result_list = result.as_list()
+ >>> type(result_list)
+ <class 'list'>
+ >>> print(result_list)
+ ['sldkj', 'lsdkj', 'sldkj']
+
+ .. versionchanged:: 3.2.0
+ New ``flatten`` argument.
"""
def flattened(pr):
@@ -545,21 +623,33 @@ class ParseResults:
"""
Returns the named parse results as a nested dictionary.
- Example::
+ Example:
+
+ .. doctest::
- integer = Word(nums)
- date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+ >>> integer = pp.Word(pp.nums)
+ >>> date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
- result = date_str.parse_string('12/31/1999')
- print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
+ >>> result = date_str.parse_string('1999/12/31')
+ >>> type(result)
+ <class 'pyparsing.results.ParseResults'>
+ >>> result
+ ParseResults(['1999', '/', '12', '/', '31'], {'year': '1999', 'month': '12', 'day': '31'})
- result_dict = result.as_dict()
- print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'}
+ >>> result_dict = result.as_dict()
+ >>> type(result_dict)
+ <class 'dict'>
+ >>> result_dict
+ {'year': '1999', 'month': '12', 'day': '31'}
- # even though a ParseResults supports dict-like access, sometime you just need to have a dict
- import json
- print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
- print(json.dumps(result.as_dict())) # -> {"month": "31", "day": "1999", "year": "12"}
+ >>> # even though a ParseResults supports dict-like access,
+ >>> # sometime you just need to have a dict
+ >>> import json
+ >>> print(json.dumps(result))
+ Traceback (most recent call last):
+ TypeError: Object of type ParseResults is not JSON serializable
+ >>> print(json.dumps(result.as_dict()))
+ {"year": "1999", "month": "12", "day": "31"}
"""
def to_item(obj):
@@ -572,10 +662,10 @@ class ParseResults:
def copy(self) -> ParseResults:
"""
- Returns a new shallow copy of a :class:`ParseResults` object. `ParseResults`
- items contained within the source are shared with the copy. Use
- :class:`ParseResults.deepcopy()` to create a copy with its own separate
- content values.
+ Returns a new shallow copy of a :class:`ParseResults` object.
+ :class:`ParseResults` items contained within the source are
+ shared with the copy. Use :meth:`ParseResults.deepcopy` to
+ create a copy with its own separate content values.
"""
ret = ParseResults(self._toklist)
ret._tokdict = self._tokdict.copy()
@@ -587,6 +677,8 @@ class ParseResults:
def deepcopy(self) -> ParseResults:
"""
Returns a new deep copy of a :class:`ParseResults` object.
+
+ .. versionadded:: 3.1.0
"""
ret = self.copy()
# replace values with copies if they are of known mutable types
@@ -607,28 +699,35 @@ class ParseResults:
def get_name(self) -> str | None:
r"""
- Returns the results name for this token expression. Useful when several
- different expressions might match at a particular location.
+ Returns the results name for this token expression.
+
+ Useful when several different expressions might match
+ at a particular location.
+
+ Example:
+
+ .. testcode::
+
+ integer = Word(nums)
+ ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
+ house_number_expr = Suppress('#') + Word(nums, alphanums)
+ user_data = (Group(house_number_expr)("house_number")
+ | Group(ssn_expr)("ssn")
+ | Group(integer)("age"))
+ user_info = user_data[1, ...]
- Example::
+ result = user_info.parse_string("22 111-22-3333 #221B")
+ for item in result:
+ print(item.get_name(), ':', item[0])
- integer = Word(nums)
- ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
- house_number_expr = Suppress('#') + Word(nums, alphanums)
- user_data = (Group(house_number_expr)("house_number")
- | Group(ssn_expr)("ssn")
- | Group(integer)("age"))
- user_info = user_data[1, ...]
+ prints:
- result = user_info.parse_string("22 111-22-3333 #221B")
- for item in result:
- print(item.get_name(), ':', item[0])
+ .. testoutput::
- prints::
+ age : 22
+ ssn : 111-22-3333
+ house_number : 221B
- age : 22
- ssn : 111-22-3333
- house_number : 221B
"""
if self._name:
return self._name
@@ -659,20 +758,24 @@ class ParseResults:
a :class:`ParseResults`. Accepts an optional ``indent`` argument so
that this string can be embedded in a nested display of other data.
- Example::
+ Example:
- integer = Word(nums)
- date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+ .. testcode::
- result = date_str.parse_string('1999/12/31')
- print(result.dump())
+ integer = Word(nums)
+ date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
- prints::
+ result = date_str.parse_string('1999/12/31')
+ print(result.dump())
- ['1999', '/', '12', '/', '31']
- - day: '31'
- - month: '12'
- - year: '1999'
+ prints:
+
+ .. testoutput::
+
+ ['1999', '/', '12', '/', '31']
+ - day: '31'
+ - month: '12'
+ - year: '1999'
"""
out = []
NL = "\n"
@@ -734,23 +837,27 @@ class ParseResults:
Accepts additional positional or keyword args as defined for
`pprint.pprint <https://docs.python.org/3/library/pprint.html#pprint.pprint>`_ .
- Example::
+ Example:
+
+ .. testcode::
- ident = Word(alphas, alphanums)
- num = Word(nums)
- func = Forward()
- term = ident | num | Group('(' + func + ')')
- func <<= ident + Group(Optional(DelimitedList(term)))
- result = func.parse_string("fna a,b,(fnb c,d,200),100")
- result.pprint(width=40)
+ ident = Word(alphas, alphanums)
+ num = Word(nums)
+ func = Forward()
+ term = ident | num | Group('(' + func + ')')
+ func <<= ident + Group(Optional(DelimitedList(term)))
+ result = func.parse_string("fna a,b,(fnb c,d,200),100")
+ result.pprint(width=40)
- prints::
+ prints:
- ['fna',
- ['a',
- 'b',
- ['(', 'fnb', ['c', 'd', '200'], ')'],
- '100']]
+ .. testoutput::
+
+ ['fna',
+ ['a',
+ 'b',
+ ['(', 'fnb', ['c', 'd', '200'], ')'],
+ '100']]
"""
pprint.pprint(self.as_list(), *args, **kwargs)
@@ -780,9 +887,9 @@ class ParseResults:
@classmethod
def from_dict(cls, other, name=None) -> ParseResults:
"""
- Helper classmethod to construct a ``ParseResults`` from a ``dict``, preserving the
+ Helper classmethod to construct a :class:`ParseResults` from a ``dict``, preserving the
name-value relations as results names. If an optional ``name`` argument is
- given, a nested ``ParseResults`` will be returned.
+ given, a nested :class:`ParseResults` will be returned.
"""
def is_iterable(obj):
@@ -805,11 +912,20 @@ class ParseResults:
return ret
asList = as_list
- """Deprecated - use :class:`as_list`"""
+ """
+ .. deprecated:: 3.0.0
+ use :meth:`as_list`
+ """
asDict = as_dict
- """Deprecated - use :class:`as_dict`"""
+ """
+ .. deprecated:: 3.0.0
+ use :meth:`as_dict`
+ """
getName = get_name
- """Deprecated - use :class:`get_name`"""
+ """
+ .. deprecated:: 3.0.0
+ use :meth:`get_name`
+ """
MutableMapping.register(ParseResults)
diff --git a/contrib/python/pyparsing/py3/pyparsing/testing.py b/contrib/python/pyparsing/py3/pyparsing/testing.py
index 836b2f86fbe..7def5d37b87 100644
--- a/contrib/python/pyparsing/py3/pyparsing/testing.py
+++ b/contrib/python/pyparsing/py3/pyparsing/testing.py
@@ -24,24 +24,37 @@ class pyparsing_test:
Context manager to be used when writing unit tests that modify pyparsing config values:
- packrat parsing
- bounded recursion parsing
- - default whitespace characters.
+ - default whitespace characters
- default keyword characters
- literal string auto-conversion class
- - __diag__ settings
+ - ``__diag__`` settings
- Example::
+ Example:
- with reset_pyparsing_context():
- # test that literals used to construct a grammar are automatically suppressed
- ParserElement.inlineLiteralsUsing(Suppress)
+ .. testcode::
- term = Word(alphas) | Word(nums)
- group = Group('(' + term[...] + ')')
+ ppt = pyparsing.pyparsing_test
- # assert that the '()' characters are not included in the parsed tokens
- self.assertParseAndCheckList(group, "(abc 123 def)", ['abc', '123', 'def'])
+ class MyTestClass(ppt.TestParseResultsAsserts):
+ def test_literal(self):
+ with ppt.reset_pyparsing_context():
+ # test that literals used to construct
+ # a grammar are automatically suppressed
+ ParserElement.inline_literals_using(Suppress)
- # after exiting context manager, literals are converted to Literal expressions again
+ term = Word(alphas) | Word(nums)
+ group = Group('(' + term[...] + ')')
+
+ # assert that the '()' characters
+ # are not included in the parsed tokens
+ self.assertParseAndCheckList(
+ group,
+ "(abc 123 def)",
+ ['abc', '123', 'def']
+ )
+
+ # after exiting context manager, literals
+ # are converted to Literal expressions again
"""
def __init__(self):
@@ -145,7 +158,7 @@ class pyparsing_test:
):
"""
Convenience wrapper assert to test a parser element and input string, and assert that
- the resulting ``ParseResults.asList()`` is equal to the ``expected_list``.
+ the resulting :meth:`ParseResults.as_list` is equal to the ``expected_list``.
"""
result = expr.parse_string(test_string, parse_all=True)
if verbose:
@@ -159,7 +172,7 @@ class pyparsing_test:
):
"""
Convenience wrapper assert to test a parser element and input string, and assert that
- the resulting ``ParseResults.asDict()`` is equal to the ``expected_dict``.
+ the resulting :meth:`ParseResults.as_dict` is equal to the ``expected_dict``.
"""
result = expr.parse_string(test_string, parseAll=True)
if verbose:
@@ -172,13 +185,20 @@ class pyparsing_test:
self, run_tests_report, expected_parse_results=None, msg=None
):
"""
- Unit test assertion to evaluate output of ``ParserElement.runTests()``. If a list of
- list-dict tuples is given as the ``expected_parse_results`` argument, then these are zipped
- with the report tuples returned by ``runTests`` and evaluated using ``assertParseResultsEquals``.
- Finally, asserts that the overall ``runTests()`` success value is ``True``.
+ Unit test assertion to evaluate output of
+ :meth:`~ParserElement.run_tests`.
+
+ If a list of list-dict tuples is given as the
+ ``expected_parse_results`` argument, then these are zipped
+ with the report tuples returned by ``run_tests()``
+ and evaluated using :meth:`assertParseResultsEquals`.
+ Finally, asserts that the overall
+ `:meth:~ParserElement.run_tests` success value is ``True``.
- :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests
- :param expected_parse_results (optional): [tuple(str, list, dict, Exception)]
+ :param run_tests_report: the return value from :meth:`ParserElement.run_tests`
+ :type run_tests_report: tuple[bool, list[tuple[str, ParseResults | Exception]]]
+ :param expected_parse_results: (optional)
+ :type expected_parse_results: list[tuple[str | list | dict | Exception, ...]]
"""
run_test_success, run_test_results = run_tests_report
@@ -266,23 +286,29 @@ class pyparsing_test:
(Line and column numbers are 1-based by default - if debugging a parse action,
pass base_1=False, to correspond to the loc value passed to the parse action.)
- :param s: tuple(bool, str - string to be printed with line and column numbers
- :param start_line: int - (optional) starting line number in s to print (default=1)
- :param end_line: int - (optional) ending line number in s to print (default=len(s))
- :param expand_tabs: bool - (optional) expand tabs to spaces, to match the pyparsing default
- :param eol_mark: str - (optional) string to mark the end of lines, helps visualize trailing spaces (default="|")
- :param mark_spaces: str - (optional) special character to display in place of spaces
- :param mark_control: str - (optional) convert non-printing control characters to a placeholding
- character; valid values:
- - "unicode" - replaces control chars with Unicode symbols, such as "␍" and "␊"
- - any single character string - replace control characters with given string
- - None (default) - string is displayed as-is
- :param indent: str | int - (optional) string to indent with line and column numbers; if an int
- is passed, converted to " " * indent
- :param base_1: bool - (optional) whether to label string using base 1; if False, string will be
- labeled based at 0 (default=True)
+ :param s: string to be printed with line and column numbers
+ :param start_line: starting line number in s to print (default=1)
+ :param end_line: ending line number in s to print (default=len(s))
+ :param expand_tabs: expand tabs to spaces, to match the pyparsing default
+ :param eol_mark: string to mark the end of lines, helps visualize trailing spaces
+ :param mark_spaces: special character to display in place of spaces
+ :param mark_control: convert non-printing control characters to a placeholding
+ character; valid values:
+
+ - ``"unicode"`` - replaces control chars with Unicode symbols, such as "␍" and "␊"
+ - any single character string - replace control characters with given string
+ - ``None`` (default) - string is displayed as-is
+
+
+ :param indent: string to indent with line and column numbers; if an int
+ is passed, converted to ``" " * indent``
+ :param base_1: whether to label string using base 1; if False, string will be
+ labeled based at 0
+
+ :returns: input string with leading line numbers and column number headers
- :return: str - input string with leading line numbers and column number headers
+ .. versionchanged:: 3.2.0
+ New ``indent`` and ``base_1`` arguments.
"""
if expand_tabs:
s = s.expandtabs()
diff --git a/contrib/python/pyparsing/py3/pyparsing/util.py b/contrib/python/pyparsing/py3/pyparsing/util.py
index 1cb16e2e620..7909240bbd1 100644
--- a/contrib/python/pyparsing/py3/pyparsing/util.py
+++ b/contrib/python/pyparsing/py3/pyparsing/util.py
@@ -45,7 +45,7 @@ def col(loc: int, strg: str) -> int:
Note: the default parsing behavior is to expand tabs in the input string
before starting the parsing process. See
- :class:`ParserElement.parse_string` for more
+ :meth:`ParserElement.parse_string` for more
information on parsing strings containing ``<TAB>`` s, and suggested
methods to maintain a consistent view of the parsed string, the parse
location, and line and column positions within the parsed string.
@@ -60,7 +60,7 @@ def lineno(loc: int, strg: str) -> int:
The first line is number 1.
Note - the default parsing behavior is to expand tabs in the input string
- before starting the parsing process. See :class:`ParserElement.parse_string`
+ before starting the parsing process. See :meth:`ParserElement.parse_string`
for more information on parsing strings containing ``<TAB>`` s, and
suggested methods to maintain a consistent view of the parsed string, the
parse location, and line and column positions within the parsed string.
@@ -186,12 +186,24 @@ class _GroupConsecutive:
"""
Used as a callable `key` for itertools.groupby to group
characters that are consecutive:
- itertools.groupby("abcdejkmpqrs", key=IsConsecutive())
- yields:
- (0, iter(['a', 'b', 'c', 'd', 'e']))
- (1, iter(['j', 'k']))
- (2, iter(['m']))
- (3, iter(['p', 'q', 'r', 's']))
+
+ .. testcode::
+
+ from itertools import groupby
+ from pyparsing.util import _GroupConsecutive
+
+ grouped = groupby("abcdejkmpqrs", key=_GroupConsecutive())
+ for index, group in grouped:
+ print(tuple([index, list(group)]))
+
+ prints:
+
+ .. testoutput::
+
+ (0, ['a', 'b', 'c', 'd', 'e'])
+ (1, ['j', 'k'])
+ (2, ['m'])
+ (3, ['p', 'q', 'r', 's'])
"""
def __init__(self) -> None:
@@ -214,21 +226,28 @@ def _collapse_string_to_ranges(
Take a string or list of single-character strings, and return
a string of the consecutive characters in that string collapsed
into groups, as might be used in a regular expression '[a-z]'
- character set:
+ character set::
+
'a' -> 'a' -> '[a]'
'bc' -> 'bc' -> '[bc]'
'defgh' -> 'd-h' -> '[d-h]'
'fdgeh' -> 'd-h' -> '[d-h]'
'jklnpqrtu' -> 'j-lnp-rtu' -> '[j-lnp-rtu]'
- Duplicates get collapsed out:
+
+ Duplicates get collapsed out::
+
'aaa' -> 'a' -> '[a]'
'bcbccb' -> 'bc' -> '[bc]'
'defghhgf' -> 'd-h' -> '[d-h]'
'jklnpqrjjjtu' -> 'j-lnp-rtu' -> '[j-lnp-rtu]'
- Spaces are preserved:
+
+ Spaces are preserved::
+
'ab c' -> ' a-c' -> '[ a-c]'
+
Characters that are significant when defining regex ranges
- get escaped:
+ get escaped::
+
'acde[]-' -> r'\-\[\]ac-e' -> r'[\-\[\]ac-e]'
"""
@@ -425,7 +444,10 @@ def replaced_by_pep8(compat_name: str, fn: C) -> C:
# )
return fn(*args, **kwargs)
- _inner.__doc__ = f"""Deprecated - use :class:`{fn.__name__}`"""
+ _inner.__doc__ = f"""
+ .. deprecated:: 3.0.0
+ Use :class:`{fn.__name__}` instead
+ """
_inner.__name__ = compat_name
_inner.__annotations__ = fn.__annotations__
if isinstance(fn, types.FunctionType):
diff --git a/contrib/python/pyparsing/py3/ya.make b/contrib/python/pyparsing/py3/ya.make
index 13dd585e164..2d439e9d7f9 100644
--- a/contrib/python/pyparsing/py3/ya.make
+++ b/contrib/python/pyparsing/py3/ya.make
@@ -4,7 +4,7 @@ PY3_LIBRARY()
PROVIDES(pyparsing)
-VERSION(3.2.3)
+VERSION(3.2.5)
LICENSE(MIT)