diff options
| -rw-r--r-- | docs/agogo/static/agogo.css_t | 10 | ||||
| -rw-r--r-- | docs/source/analyzing.rst | 64 | ||||
| -rw-r--r-- | docs/source/api.rst | 28 | ||||
| -rw-r--r-- | docs/source/cmdline.rst | 8 | ||||
| -rw-r--r-- | docs/source/index.rst | 3 | ||||
| -rw-r--r-- | docs/source/ui.rst | 15 | ||||
| -rw-r--r-- | sqlparse/__init__.py | 11 | ||||
| -rw-r--r-- | sqlparse/sql.py | 96 |
8 files changed, 214 insertions, 21 deletions
diff --git a/docs/agogo/static/agogo.css_t b/docs/agogo/static/agogo.css_t index afb830a..97dc91b 100644 --- a/docs/agogo/static/agogo.css_t +++ b/docs/agogo/static/agogo.css_t @@ -167,6 +167,16 @@ div.document .descname { font-weight: bold; } +div.document .docutils.literal { + background-color: #eeeeec; + padding: 1px; +} + +div.document .docutils.xref.literal { + background-color: transparent; + padding: 0px; +} + /* Sidebar */ diff --git a/docs/source/analyzing.rst b/docs/source/analyzing.rst new file mode 100644 index 0000000..07b1fe9 --- /dev/null +++ b/docs/source/analyzing.rst @@ -0,0 +1,64 @@ +.. _analyze: + +Analyzing the Parsed Statement +============================== + +When the :meth:`~sqlparse.parse` function is called the returned value +is a tree-ish representation of the analyzed statements. The returned +objects can be used by applications to retrieve further information about +the parsed SQL. + + +Base Classes +------------ + +All returned objects inherit from these base classes. +The :class:`~sqlparse.sql.Token` class represents a single token and +:class:`~sqlparse.sql.TokenList` class is a group of tokens. +The latter provides methods for inspecting it's child tokens. + +.. autoclass:: sqlparse.sql.Token + :members: + +.. autoclass:: sqlparse.sql.TokenList + :members: + + +SQL Representing Classes +------------------------ + +The following classes represent distinct parts of a SQL statement. + +.. autoclass:: sqlparse.sql.Statement + :members: + +.. autoclass:: sqlparse.sql.Comment + :members: + +.. autoclass:: sqlparse.sql.Identifier + :members: + +.. autoclass:: sqlparse.sql.IdentifierList + :members: + +.. autoclass:: sqlparse.sql.Where + :members: + +.. autoclass:: sqlparse.sql.Case + :members: + +.. autoclass:: sqlparse.sql.Parenthesis + :members: + +.. autoclass:: sqlparse.sql.If + :members: + +.. autoclass:: sqlparse.sql.For + :members: + +.. autoclass:: sqlparse.sql.Assignment + :members: + +.. autoclass:: sqlparse.sql.Comparsion + :members: + diff --git a/docs/source/api.rst b/docs/source/api.rst index 1de31c0..2531c9b 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -1,6 +1,9 @@ :mod:`sqlparse` -- Parse SQL statements ======================================= +.. module:: sqlparse + :synopsis: Parse SQL statements. + The :mod:`sqlparse` module provides the following functions on module-level. .. autofunction:: sqlparse.split @@ -15,9 +18,28 @@ The :mod:`sqlparse` module provides the following functions on module-level. Formatting of SQL Statements ---------------------------- +The :meth:`~sqlparse.format` function accepts the following keyword arguments. + +``keyword_case`` + Changes how keywords are formatted. Allowed values are "upper", "lower" + and "capitalize". + +``identifier_case`` + Changes how identifiers are formatted. Allowed values are "upper", "lower", + and "capitalize". + +``strip_comments`` + If ``True`` comments are removed from the statements. + +``reindent`` + If ``True`` the indentations of the statements are changed. -.. _analyze: +``indent_tabs`` + If ``True`` tabs instead of spaces are used for indentation. -Analyzing the Parsed Statement ------------------------------- +``indent_width`` + The width of the indentation, defaults to 2. +``output_format`` + If given the output is additionally formatted to be used as a variable + in a programming language. Allowed values are "python" and "php". diff --git a/docs/source/cmdline.rst b/docs/source/cmdline.rst deleted file mode 100644 index 93a35a7..0000000 --- a/docs/source/cmdline.rst +++ /dev/null @@ -1,8 +0,0 @@ -``sqlformat`` -- Command Line Script -=========================================== - -The :mod:`sqlparse` module is shipped with the script -:program:`sqlformat` which provides a command line interface to the formatting -functions. - -.. todo:: Describe options and example usage. diff --git a/docs/source/index.rst b/docs/source/index.rst index 0e74763..44d03a2 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -11,7 +11,8 @@ python-sqlparse's documentation contents intro api - cmdline + analyzing + ui changes diff --git a/docs/source/ui.rst b/docs/source/ui.rst new file mode 100644 index 0000000..264916e --- /dev/null +++ b/docs/source/ui.rst @@ -0,0 +1,15 @@ +User Interfaces +=============== + +``sqlformat`` + The ``sqlformat`` command line script ist distributed with the module. + Run :command:`sqlformat --help` to list available options and for usage + hints. + +``sqlformat.appspot.com`` + An example `Google App Engine <http://code.google.com/appengine/>`_ + application that exposes the formatting features using a web front-end. + See http://sqlformat.appspot.com for details. + The source for this application is available from a source code check out + of the :mod:`sqlparse` module (see :file:`extras/appengine`). + diff --git a/sqlparse/__init__.py b/sqlparse/__init__.py index 01b3bd8..9a97dc0 100644 --- a/sqlparse/__init__.py +++ b/sqlparse/__init__.py @@ -30,9 +30,7 @@ def parse(sql): *sql* is a single string containting one or more SQL statements. - The returned :class:`~sqlparse.parser.Statement` are fully analyzed. - - Returns a list of :class:`~sqlparse.parser.Statement` instances. + Returns a tuple of :class:`~sqlparse.sql.Statement` instances. """ stack = engine.FilterStack() stack.full_analyze() @@ -42,10 +40,9 @@ def parse(sql): def format(sql, **options): """Format *sql* according to *options*. - Returns a list of :class:`~sqlparse.parse.Statement` instances like - :meth:`parse`, but the statements are formatted according to *options*. + Available options are documented in :ref:`formatting`. - Available options are documented in the :mod:`~sqlparse.format` module. + Returns the formatted SQL statement as string. """ stack = engine.FilterStack() options = formatter.validate_options(options) @@ -55,7 +52,7 @@ def format(sql, **options): def split(sql): - """Split *sql* into separate statements. + """Split *sql* into single statements. Returns a list of strings. """ diff --git a/sqlparse/sql.py b/sqlparse/sql.py index 372a3c7..db9f1c1 100644 --- a/sqlparse/sql.py +++ b/sqlparse/sql.py @@ -9,6 +9,12 @@ from sqlparse import tokens as T class Token(object): + """Base class for all other classes in this module. + + It represents a single token and has two instance attributes: + ``value`` is the unchange value of the token and ``ttype`` is + the type of the token. + """ __slots__ = ('value', 'ttype') @@ -28,6 +34,7 @@ class Token(object): return self.value def to_unicode(self): + """Returns a unicode representation of this object.""" return unicode(self) def _get_repr_name(self): @@ -42,6 +49,17 @@ class Token(object): return re.sub('\s+', ' ', short) def match(self, ttype, values, regex=False): + """Checks whether the token matches the given arguments. + + *ttype* is a token type. If this token doesn't match the given token + type. + *values* is a list of possible values for this token. The values + are OR'ed together so if only one of the values matches ``True`` + is returned. Except for keyword tokens the comparsion is + case-sensitive. For convenience it's ok to pass in a single string. + If *regex* is ``True`` (default is ``False``) the given values are + treated as regular expressions. + """ if self.ttype is not ttype: return False if values is None: @@ -64,13 +82,20 @@ class Token(object): return self.value in values def is_group(self): + """Returns ``True`` if this object has children.""" return False def is_whitespace(self): + """Return ``True`` if this token is a whitespace token.""" return self.ttype and self.ttype in T.Whitespace class TokenList(Token): + """A group of tokens. + + It has an additional instance attribute ``tokens`` which holds a + list of child-tokens. + """ __slots__ = ('value', 'ttype', 'tokens') @@ -104,6 +129,10 @@ class TokenList(Token): token._pprint_tree(max_depth, depth+1) def flatten(self): + """Generator yielding ungrouped tokens. + + This method is recursively called for all child tokens. + """ for token in self.tokens: if isinstance(token, TokenList): for item in token.flatten(): @@ -118,6 +147,11 @@ class TokenList(Token): return [x for x in self.tokens if isinstance(x, TokenList)] def token_first(self, ignore_whitespace=True): + """Returns the first child token. + + If *ignore_whitespace* is ``True`` (the default), whitespace + tokens are ignored. + """ for token in self.tokens: if ignore_whitespace and token.is_whitespace(): continue @@ -125,6 +159,13 @@ class TokenList(Token): return None def token_next_by_instance(self, idx, clss): + """Returns the next token matching a class. + + *idx* is where to start searching in the list of child tokens. + *clss* is a list of classes the token should be an instance of. + + If no matching token can be found ``None`` is returned. + """ if type(clss) not in (types.ListType, types.TupleType): clss = (clss,) if type(clss) is not types.TupleType: @@ -135,6 +176,7 @@ class TokenList(Token): return None def token_next_by_type(self, idx, ttypes): + """Returns next matching token by it's token type.""" if not isinstance(ttypes, (types.TupleType, types.ListType)): ttypes = [ttypes] for token in self.tokens[idx:]: @@ -143,6 +185,7 @@ class TokenList(Token): return None def token_next_match(self, idx, ttype, value, regex=False): + """Returns next token where it's ``match`` method returns ``True``.""" if type(idx) != types.IntType: idx = self.token_index(idx) for token in self.tokens[idx:]: @@ -162,6 +205,11 @@ class TokenList(Token): return None def token_prev(self, idx, skip_ws=True): + """Returns the previous token relative to *idx*. + + If *skip_ws* is ``True`` (the default) whitespace tokens are ignored. + ``None`` is returned if there's no previous token. + """ while idx != 0: idx -= 1 if self.tokens[idx].is_whitespace() and skip_ws: @@ -169,6 +217,11 @@ class TokenList(Token): return self.tokens[idx] def token_next(self, idx, skip_ws=True): + """Returns the next token relative to *idx*. + + If *skip_ws* is ``True`` (the default) whitespace tokens are ignored. + ``None`` is returned if there's no next token. + """ while idx < len(self.tokens)-1: idx += 1 if self.tokens[idx].is_whitespace() and skip_ws: @@ -180,7 +233,11 @@ class TokenList(Token): return self.tokens.index(token) def tokens_between(self, start, end, exclude_end=False): - """Return all tokens between (and including) start and end.""" + """Return all tokens between (and including) start and end. + + If *exclude_end* is ``True`` (default is ``False``) the end token + is included too. + """ if exclude_end: offset = 0 else: @@ -188,7 +245,7 @@ class TokenList(Token): return self.tokens[self.token_index(start):self.token_index(end)+offset] def group_tokens(self, grp_cls, tokens): - """Replace tokens by instance of grp_cls.""" + """Replace tokens by an instance of *grp_cls*.""" idx = self.token_index(tokens[0]) for t in tokens: self.tokens.remove(t) @@ -197,14 +254,22 @@ class TokenList(Token): return grp def insert_before(self, where, token): + """Inserts *token* before *where*.""" self.tokens.insert(self.token_index(where), token) class Statement(TokenList): + """Represents a SQL statement.""" __slots__ = ('value', 'ttype', 'tokens') def get_type(self): + """Returns the type of a statement. + + The returned value is a string holding an upper-cased reprint of + the first DML or DDL keyword. If the first token in this group + isn't a DML or DDL keyword "UNKNOWN" is returned. + """ first_token = self.token_first() if first_token.ttype in (T.Keyword.DML, T.Keyword.DDL): return first_token.value.upper() @@ -213,13 +278,19 @@ class Statement(TokenList): class Identifier(TokenList): + """Represents an identifier. + + Identifiers may have aliases or typecasts. + """ __slots__ = ('value', 'ttype', 'tokens') def has_alias(self): + """Returns ``True`` if an alias is present.""" return self.get_alias() is not None def get_alias(self): + """Returns the alias for this identifier or ``None``.""" kw = self.token_next_match(0, T.Keyword, 'AS') if kw is not None: alias = self.token_next(self.token_index(kw)) @@ -236,15 +307,23 @@ class Identifier(TokenList): return alias.to_unicode() def get_name(self): + """Returns the name of this identifier. + + This is either it's alias or it's real name. The returned valued can + be considered as the name under which the object corresponding to + this identifier is known within the current statement. + """ alias = self.get_alias() if alias is not None: return alias return self.get_real_name() def get_real_name(self): + """Returns the real name (object name) of this identifier.""" return self.token_next_by_type(0, T.Name).value def get_typecast(self): + """Returns the typecast or ``None`` of this object as a string.""" marker = self.token_next_match(0, T.Punctuation, '::') if marker is None: return None @@ -255,37 +334,50 @@ class Identifier(TokenList): class IdentifierList(TokenList): + """A list of :class:`~sqlparse.sql.Identifier`\'s.""" __slots__ = ('value', 'ttype', 'tokens') def get_identifiers(self): + """Returns the identifiers. + + Whitespaces and punctuations are not included in this list. + """ return [x for x in self.tokens if isinstance(x, Identifier)] class Parenthesis(TokenList): + """Tokens between parenthesis.""" __slots__ = ('value', 'ttype', 'tokens') class Assignment(TokenList): + """An assignment like 'var := val;'""" __slots__ = ('value', 'ttype', 'tokens') class If(TokenList): + """An 'if' clause with possible 'else if' or 'else' parts.""" __slots__ = ('value', 'ttype', 'tokens') class For(TokenList): + """A 'FOR' loop.""" __slots__ = ('value', 'ttype', 'tokens') class Comparsion(TokenList): + """A comparsion used for example in WHERE clauses.""" __slots__ = ('value', 'ttype', 'tokens') class Comment(TokenList): + """A comment.""" __slots__ = ('value', 'ttype', 'tokens') class Where(TokenList): + """A WHERE clause.""" __slots__ = ('value', 'ttype', 'tokens') class Case(TokenList): + """A CASE statement with one or more WHEN and possibly an ELSE part.""" __slots__ = ('value', 'ttype', 'tokens') |
