Skip to content

Commit

Permalink
Get rid of the old Node wrappers
Browse files Browse the repository at this point in the history
Those wrappers were needed in v1 to hide the underlying JSON structures
emitted by libpg_query, making it possible to see them as generic Python
instances.

Version 3 obsoleted that by introducing a set of concrete AST classes,
and the wrappers were kept mainly for their ability to track the
parentship of each node, required by some of the printer functions.

Now they are gone, and everything works directly on AST classes, and the
printers use the new "ancestors" slot attached to each instance.

This implements issue #80.
  • Loading branch information
lelit committed May 28, 2022
1 parent 901c75e commit 01a83ab
Show file tree
Hide file tree
Showing 15 changed files with 395 additions and 995 deletions.
3 changes: 1 addition & 2 deletions docs/api.rst
Expand Up @@ -3,7 +3,7 @@
.. :Created: gio 10 ago 2017 10:14:17 CEST
.. :Author: Lele Gaifax <lele@metapensiero.it>
.. :License: GNU General Public License version 3 or later
.. :Copyright: © 2017, 2018, 2019, 2021 Lele Gaifax
.. :Copyright: © 2017, 2018, 2019, 2021, 2022 Lele Gaifax
..
===================
Expand All @@ -23,7 +23,6 @@ This chapter briefly explains some implementation detail.
ast
enums
keywords
node
stream
printers
sfuncs
Expand Down
38 changes: 0 additions & 38 deletions docs/node.rst

This file was deleted.

4 changes: 2 additions & 2 deletions docs/printers.rst
Expand Up @@ -3,7 +3,7 @@
.. :Created: gio 10 ago 2017 13:23:18 CEST
.. :Author: Lele Gaifax <lele@metapensiero.it>
.. :License: GNU General Public License version 3 or later
.. :Copyright: © 2017, 2018, 2021 Lele Gaifax
.. :Copyright: © 2017, 2018, 2021, 2022 Lele Gaifax
..
==========================================================
Expand All @@ -22,7 +22,7 @@ associated :class:`~.node.Node` will be serialized.

.. autoexception:: PrinterAlreadyPresentError

.. autofunction:: get_printer_for_node_tag
.. autofunction:: get_printer_for_node

.. autofunction:: node_printer

Expand Down
96 changes: 4 additions & 92 deletions docs/usage.rst
Expand Up @@ -3,7 +3,7 @@
.. :Created: gio 10 ago 2017 10:06:38 CEST
.. :Author: Lele Gaifax <lele@metapensiero.it>
.. :License: GNU General Public License version 3 or later
.. :Copyright: © 2017, 2018, 2019, 2021 Lele Gaifax
.. :Copyright: © 2017, 2018, 2019, 2021, 2022 Lele Gaifax
..
.. _usage:
Expand All @@ -15,7 +15,7 @@
Here are some example of how the module can be used.

---------
Low level
AST level
---------

The lowest level is a Python wrapper around each *parse node* returned by the ``PostgreSQL``
Expand Down Expand Up @@ -181,89 +181,6 @@ This basically means that you can reconstruct a syntax tree from the result of c
>>> clone == stmt
True

------------
Medium level
------------

Parse an ``SQL`` statement and get its *AST* root node
======================================================

.. doctest::

>>> from pglast import Node
>>> root = Node(parse_sql('SELECT foo FROM bar'))
>>> print(root)
None=[1*{RawStmt}]

Get a particular node
=====================

.. doctest::

>>> from_clause = root[0].stmt.fromClause
>>> print(from_clause)
fromClause=[1*{RangeVar}]

Obtain some information about a node
====================================

.. doctest::

>>> range_var = from_clause[0]
>>> print(range_var.node_tag)
RangeVar
>>> print(range_var.attribute_names)
('catalogname', 'schemaname', 'relname', 'inh', 'relpersistence', 'alias', 'location')
>>> print(range_var.parent_node)
stmt={SelectStmt}

Iterate over nodes
==================

.. doctest::

>>> for a in from_clause:
... print(a)
... for b in a:
... print(b)
...
fromClause[0]={RangeVar}
inh=<True>
location=<16>
relname=<'bar'>
relpersistence=<'p'>

Recursively :meth:`traverse <pglast.node.Node.traverse>` the parse tree
=======================================================================

.. doctest::

>>> for node in root.traverse():
... print(node)
...
None[0]={RawStmt}
stmt={SelectStmt}
all=<False>
fromClause[0]={RangeVar}
inh=<True>
location=<16>
relname=<'bar'>
relpersistence=<'p'>
limitOption=<LimitOption.LIMIT_OPTION_DEFAULT: 0>
op=<SetOperation.SETOP_NONE: 0>
targetList[0]={ResTarget}
location=<7>
val={ColumnRef}
fields[0]={String}
val=<'foo'>
location=<7>
stmt_len=<0>
stmt_location=<0>

As you can see, the ``repr``\ esentation of each value is mnemonic: ``{some_tag}`` means a
``Node`` with tag ``some_tag``, ``[X*{some_tag}]`` is a ``List`` containing `X` nodes of that
particular kind\ [*]_ and ``<value>`` is a ``Scalar``.

Programmatically :func:`reformat <pglast.prettify>` a ``SQL`` statement
=======================================================================

Expand Down Expand Up @@ -318,7 +235,7 @@ that extends :class:`pglast.stream.RawStream` adding a bit a aesthetic sense.

.. doctest::

>>> print(IndentedStream()(root))
>>> print(IndentedStream()('select foo from bar'))
SELECT foo
FROM bar

Expand Down Expand Up @@ -400,7 +317,7 @@ Customize a :func:`node printer <pglast.printers.node_printer>`
>>> from pglast.printers import node_printer
>>> @node_printer('ParamRef', override=True)
... def replace_param_ref(node, output):
... output.write(repr(args[node.number.value - 1]))
... output.write(repr(args[node.number - 1]))
...
>>> args = ['Hello', 'Ciao']
>>> print(prettify(sql, safety_belt=False))
Expand Down Expand Up @@ -542,11 +459,6 @@ Preserve comments
carry their exact location, so it is not possible to differentiate between
``SELECT * /*comment*/ FROM foo`` and ``SELECT * FROM /*comment*/ foo``.

---

.. [*] This is an approximation, because in principle a list can contain different kinds of
nodes, or even sub-lists in some cases: the ``List`` representation arbitrarily shows
the tag of the first object.

Functions vs SQL syntax
=======================
Expand Down
13 changes: 9 additions & 4 deletions pglast/__init__.py
Expand Up @@ -3,12 +3,13 @@
# :Created: mer 02 ago 2017 15:11:02 CEST
# :Author: Lele Gaifax <lele@metapensiero.it>
# :License: GNU General Public License version 3 or later
# :Copyright: © 2017, 2018, 2019, 2021 Lele Gaifax
# :Copyright: © 2017, 2018, 2019, 2021, 2022 Lele Gaifax
#

from collections import namedtuple

from . import enums
from .error import Error
from .node import Comment, Missing, Node
try:
from .parser import fingerprint, get_postgresql_version, parse_sql, scan, split
except ModuleNotFoundError: # pragma: no cover
Expand All @@ -31,6 +32,10 @@ def parse_plpgsql(statement):
return loads(parse_plpgsql_json(statement))


Comment = namedtuple('Comment', ('location', 'text', 'at_start_of_line', 'continue_previous'))
"A structure to carry information about a single SQL comment."


def _extract_comments(statement):
lines = []
lofs = 0
Expand Down Expand Up @@ -81,7 +86,7 @@ def prettify(statement, safety_belt=False, preserve_comments=False, **options):
options['comments'] = _extract_comments(statement)

orig_pt = parse_sql(statement)
prettified = IndentedStream(**options)(Node(orig_pt))
prettified = IndentedStream(**options)(orig_pt)
if safety_belt:
from logging import getLogger
import warnings
Expand Down Expand Up @@ -109,5 +114,5 @@ def prettify(statement, safety_belt=False, preserve_comments=False, **options):
return prettified


__all__ = ('Error', 'Missing', 'Node', 'enums', 'fingerprint', 'get_postgresql_version',
__all__ = ('Error', 'enums', 'fingerprint', 'get_postgresql_version',
'parse_plpgsql', 'parse_sql', 'prettify', 'split')

0 comments on commit 01a83ab

Please sign in to comment.