utils.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  2. # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
  4. from __future__ import annotations
  5. import contextlib
  6. import platform
  7. import sys
  8. import traceback
  9. from collections.abc import Iterator, Sequence
  10. from datetime import datetime
  11. from pathlib import Path
  12. from pylint.constants import PYLINT_HOME, full_version
  13. def prepare_crash_report(ex: Exception, filepath: str, crash_file_path: str) -> Path:
  14. issue_template_path = (
  15. Path(PYLINT_HOME) / datetime.now().strftime(str(crash_file_path))
  16. ).resolve()
  17. with open(filepath, encoding="utf8") as f:
  18. file_content = f.read()
  19. template = ""
  20. if not issue_template_path.exists():
  21. template = """\
  22. First, please verify that the bug is not already filled:
  23. https://github.com/pylint-dev/pylint/issues/
  24. Then create a new issue:
  25. https://github.com/pylint-dev/pylint/issues/new?labels=Crash 💥%2CNeeds triage 📥
  26. """
  27. template += f"""
  28. Issue title:
  29. Crash ``{ex}`` (if possible, be more specific about what made pylint crash)
  30. ### Bug description
  31. When parsing the following ``a.py``:
  32. <!--
  33. If sharing the code is not an option, please state so,
  34. but providing only the stacktrace would still be helpful.
  35. -->
  36. ```python
  37. {file_content}
  38. ```
  39. ### Command used
  40. ```shell
  41. pylint a.py
  42. ```
  43. ### Pylint output
  44. <details open>
  45. <summary>
  46. pylint crashed with a ``{ex.__class__.__name__}`` and with the following stacktrace:
  47. </summary>
  48. ```python
  49. """
  50. template += traceback.format_exc()
  51. template += f"""
  52. ```
  53. </details>
  54. ### Expected behavior
  55. No crash.
  56. ### Pylint version
  57. ```shell
  58. {full_version}
  59. ```
  60. ### OS / Environment
  61. {sys.platform} ({platform.system()})
  62. ### Additional dependencies
  63. <!--
  64. Please remove this part if you're not using any of
  65. your dependencies in the example.
  66. -->
  67. """
  68. try:
  69. with open(issue_template_path, "a", encoding="utf8") as f:
  70. f.write(template)
  71. except Exception as exc: # pylint: disable=broad-except
  72. print(
  73. f"Can't write the issue template for the crash in {issue_template_path} "
  74. f"because of: '{exc}'\nHere's the content anyway:\n{template}.",
  75. file=sys.stderr,
  76. )
  77. return issue_template_path
  78. def get_fatal_error_message(filepath: str, issue_template_path: Path) -> str:
  79. return (
  80. f"Fatal error while checking '{filepath}'. "
  81. f"Please open an issue in our bug tracker so we address this. "
  82. f"There is a pre-filled template that you can use in '{issue_template_path}'."
  83. )
  84. def _augment_sys_path(additional_paths: Sequence[str]) -> list[str]:
  85. original = list(sys.path)
  86. changes = []
  87. seen = set()
  88. for additional_path in additional_paths:
  89. if additional_path not in seen:
  90. changes.append(additional_path)
  91. seen.add(additional_path)
  92. sys.path[:] = changes + sys.path
  93. return original
  94. @contextlib.contextmanager
  95. def augmented_sys_path(additional_paths: Sequence[str]) -> Iterator[None]:
  96. """Augment 'sys.path' by adding non-existent entries from additional_paths."""
  97. original = _augment_sys_path(additional_paths)
  98. try:
  99. yield
  100. finally:
  101. sys.path[:] = original