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.

156 lines
5.2 KiB

4 years ago
  1. from collections import namedtuple
  2. import re
  3. import sys
  4. from ast import literal_eval
  5. from parso._compatibility import unicode, total_ordering
  6. Version = namedtuple('Version', 'major, minor, micro')
  7. def split_lines(string, keepends=False):
  8. r"""
  9. Intended for Python code. In contrast to Python's :py:meth:`str.splitlines`,
  10. looks at form feeds and other special characters as normal text. Just
  11. splits ``\n`` and ``\r\n``.
  12. Also different: Returns ``[""]`` for an empty string input.
  13. In Python 2.7 form feeds are used as normal characters when using
  14. str.splitlines. However in Python 3 somewhere there was a decision to split
  15. also on form feeds.
  16. """
  17. if keepends:
  18. lst = string.splitlines(True)
  19. # We have to merge lines that were broken by form feed characters.
  20. merge = []
  21. for i, line in enumerate(lst):
  22. if line.endswith('\f'):
  23. merge.append(i)
  24. for index in reversed(merge):
  25. try:
  26. lst[index] = lst[index] + lst[index + 1]
  27. del lst[index + 1]
  28. except IndexError:
  29. # index + 1 can be empty and therefore there's no need to
  30. # merge.
  31. pass
  32. # The stdlib's implementation of the end is inconsistent when calling
  33. # it with/without keepends. One time there's an empty string in the
  34. # end, one time there's none.
  35. if string.endswith('\n') or string == '':
  36. lst.append('')
  37. return lst
  38. else:
  39. return re.split('\n|\r\n', string)
  40. def python_bytes_to_unicode(source, encoding='utf-8', errors='strict'):
  41. """
  42. Checks for unicode BOMs and PEP 263 encoding declarations. Then returns a
  43. unicode object like in :py:meth:`bytes.decode`.
  44. :param encoding: See :py:meth:`bytes.decode` documentation.
  45. :param errors: See :py:meth:`bytes.decode` documentation. ``errors`` can be
  46. ``'strict'``, ``'replace'`` or ``'ignore'``.
  47. """
  48. def detect_encoding():
  49. """
  50. For the implementation of encoding definitions in Python, look at:
  51. - http://www.python.org/dev/peps/pep-0263/
  52. - http://docs.python.org/2/reference/lexical_analysis.html#encoding-declarations
  53. """
  54. byte_mark = literal_eval(r"b'\xef\xbb\xbf'")
  55. if source.startswith(byte_mark):
  56. # UTF-8 byte-order mark
  57. return 'utf-8'
  58. first_two_lines = re.match(br'(?:[^\n]*\n){0,2}', source).group(0)
  59. possible_encoding = re.search(br"coding[=:]\s*([-\w.]+)",
  60. first_two_lines)
  61. if possible_encoding:
  62. return possible_encoding.group(1)
  63. else:
  64. # the default if nothing else has been set -> PEP 263
  65. return encoding
  66. if isinstance(source, unicode):
  67. # only cast str/bytes
  68. return source
  69. encoding = detect_encoding()
  70. if not isinstance(encoding, unicode):
  71. encoding = unicode(encoding, 'utf-8', 'replace')
  72. # Cast to unicode
  73. return unicode(source, encoding, errors)
  74. def version_info():
  75. """
  76. Returns a namedtuple of parso's version, similar to Python's
  77. ``sys.version_info``.
  78. """
  79. from parso import __version__
  80. tupl = re.findall(r'[a-z]+|\d+', __version__)
  81. return Version(*[x if i == 3 else int(x) for i, x in enumerate(tupl)])
  82. def _parse_version(version):
  83. match = re.match(r'(\d+)(?:\.(\d)(?:\.\d+)?)?$', version)
  84. if match is None:
  85. raise ValueError('The given version is not in the right format. '
  86. 'Use something like "3.2" or "3".')
  87. major = int(match.group(1))
  88. minor = match.group(2)
  89. if minor is None:
  90. # Use the latest Python in case it's not exactly defined, because the
  91. # grammars are typically backwards compatible?
  92. if major == 2:
  93. minor = "7"
  94. elif major == 3:
  95. minor = "6"
  96. else:
  97. raise NotImplementedError("Sorry, no support yet for those fancy new/old versions.")
  98. minor = int(minor)
  99. return PythonVersionInfo(major, minor)
  100. @total_ordering
  101. class PythonVersionInfo(namedtuple('Version', 'major, minor')):
  102. def __gt__(self, other):
  103. if isinstance(other, tuple):
  104. if len(other) != 2:
  105. raise ValueError("Can only compare to tuples of length 2.")
  106. return (self.major, self.minor) > other
  107. super(PythonVersionInfo, self).__gt__(other)
  108. return (self.major, self.minor)
  109. def __eq__(self, other):
  110. if isinstance(other, tuple):
  111. if len(other) != 2:
  112. raise ValueError("Can only compare to tuples of length 2.")
  113. return (self.major, self.minor) == other
  114. super(PythonVersionInfo, self).__eq__(other)
  115. def __ne__(self, other):
  116. return not self.__eq__(other)
  117. def parse_version_string(version=None):
  118. """
  119. Checks for a valid version number (e.g. `3.2` or `2.7.1` or `3`) and
  120. returns a corresponding version info that is always two characters long in
  121. decimal.
  122. """
  123. if version is None:
  124. version = '%s.%s' % sys.version_info[:2]
  125. if not isinstance(version, (unicode, str)):
  126. raise TypeError("version must be a string like 3.2.")
  127. return _parse_version(version)