You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.py 3.4KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2009-2018 the sqlparse authors and contributors
  4. # <see AUTHORS file>
  5. #
  6. # This module is part of python-sqlparse and is released under
  7. # the BSD License: https://opensource.org/licenses/BSD-3-Clause
  8. import itertools
  9. import re
  10. from collections import deque
  11. from contextlib import contextmanager
  12. from sqlparse.compat import text_type
  13. # This regular expression replaces the home-cooked parser that was here before.
  14. # It is much faster, but requires an extra post-processing step to get the
  15. # desired results (that are compatible with what you would expect from the
  16. # str.splitlines() method).
  17. #
  18. # It matches groups of characters: newlines, quoted strings, or unquoted text,
  19. # and splits on that basis. The post-processing step puts those back together
  20. # into the actual lines of SQL.
  21. SPLIT_REGEX = re.compile(r"""
  22. (
  23. (?: # Start of non-capturing group
  24. (?:\r\n|\r|\n) | # Match any single newline, or
  25. [^\r\n'"]+ | # Match any character series without quotes or
  26. # newlines, or
  27. "(?:[^"\\]|\\.)*" | # Match double-quoted strings, or
  28. '(?:[^'\\]|\\.)*' # Match single quoted strings
  29. )
  30. )
  31. """, re.VERBOSE)
  32. LINE_MATCH = re.compile(r'(\r\n|\r|\n)')
  33. def split_unquoted_newlines(stmt):
  34. """Split a string on all unquoted newlines.
  35. Unlike str.splitlines(), this will ignore CR/LF/CR+LF if the requisite
  36. character is inside of a string."""
  37. text = text_type(stmt)
  38. lines = SPLIT_REGEX.split(text)
  39. outputlines = ['']
  40. for line in lines:
  41. if not line:
  42. continue
  43. elif LINE_MATCH.match(line):
  44. outputlines.append('')
  45. else:
  46. outputlines[-1] += line
  47. return outputlines
  48. def remove_quotes(val):
  49. """Helper that removes surrounding quotes from strings."""
  50. if val is None:
  51. return
  52. if val[0] in ('"', "'") and val[0] == val[-1]:
  53. val = val[1:-1]
  54. return val
  55. def recurse(*cls):
  56. """Function decorator to help with recursion
  57. :param cls: Classes to not recurse over
  58. :return: function
  59. """
  60. def wrap(f):
  61. def wrapped_f(tlist):
  62. for sgroup in tlist.get_sublists():
  63. if not isinstance(sgroup, cls):
  64. wrapped_f(sgroup)
  65. f(tlist)
  66. return wrapped_f
  67. return wrap
  68. def imt(token, i=None, m=None, t=None):
  69. """Helper function to simplify comparisons Instance, Match and TokenType
  70. :param token:
  71. :param i: Class or Tuple/List of Classes
  72. :param m: Tuple of TokenType & Value. Can be list of Tuple for multiple
  73. :param t: TokenType or Tuple/List of TokenTypes
  74. :return: bool
  75. """
  76. clss = i
  77. types = [t, ] if t and not isinstance(t, list) else t
  78. mpatterns = [m, ] if m and not isinstance(m, list) else m
  79. if token is None:
  80. return False
  81. elif clss and isinstance(token, clss):
  82. return True
  83. elif mpatterns and any(token.match(*pattern) for pattern in mpatterns):
  84. return True
  85. elif types and any(token.ttype in ttype for ttype in types):
  86. return True
  87. else:
  88. return False
  89. def consume(iterator, n):
  90. """Advance the iterator n-steps ahead. If n is none, consume entirely."""
  91. deque(itertools.islice(iterator, n), maxlen=0)
  92. @contextmanager
  93. def offset(filter_, n=0):
  94. filter_.offset += n
  95. yield
  96. filter_.offset -= n
  97. @contextmanager
  98. def indent(filter_, n=1):
  99. filter_.indent += n
  100. yield
  101. filter_.indent -= n