# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt from __future__ import annotations import contextlib import platform import sys import traceback from collections.abc import Iterator, Sequence from datetime import datetime from pathlib import Path from pylint.constants import PYLINT_HOME, full_version def prepare_crash_report(ex: Exception, filepath: str, crash_file_path: str) -> Path: issue_template_path = ( Path(PYLINT_HOME) / datetime.now().strftime(str(crash_file_path)) ).resolve() with open(filepath, encoding="utf8") as f: file_content = f.read() template = "" if not issue_template_path.exists(): template = """\ First, please verify that the bug is not already filled: https://github.com/pylint-dev/pylint/issues/ Then create a new issue: https://github.com/pylint-dev/pylint/issues/new?labels=Crash 💥%2CNeeds triage 📥 """ template += f""" Issue title: Crash ``{ex}`` (if possible, be more specific about what made pylint crash) ### Bug description When parsing the following ``a.py``: ```python {file_content} ``` ### Command used ```shell pylint a.py ``` ### Pylint output
pylint crashed with a ``{ex.__class__.__name__}`` and with the following stacktrace: ```python """ template += traceback.format_exc() template += f""" ```
### Expected behavior No crash. ### Pylint version ```shell {full_version} ``` ### OS / Environment {sys.platform} ({platform.system()}) ### Additional dependencies """ try: with open(issue_template_path, "a", encoding="utf8") as f: f.write(template) except Exception as exc: # pylint: disable=broad-except print( f"Can't write the issue template for the crash in {issue_template_path} " f"because of: '{exc}'\nHere's the content anyway:\n{template}.", file=sys.stderr, ) return issue_template_path def get_fatal_error_message(filepath: str, issue_template_path: Path) -> str: return ( f"Fatal error while checking '{filepath}'. " f"Please open an issue in our bug tracker so we address this. " f"There is a pre-filled template that you can use in '{issue_template_path}'." ) def _augment_sys_path(additional_paths: Sequence[str]) -> list[str]: original = list(sys.path) changes = [] seen = set() for additional_path in additional_paths: if additional_path not in seen: changes.append(additional_path) seen.add(additional_path) sys.path[:] = changes + sys.path return original @contextlib.contextmanager def augmented_sys_path(additional_paths: Sequence[str]) -> Iterator[None]: """Augment 'sys.path' by adding non-existent entries from additional_paths.""" original = _augment_sys_path(additional_paths) try: yield finally: sys.path[:] = original def _is_relative_to(self: Path, *other: Path) -> bool: """Checks if self is relative to other. Backport of pathlib.Path.is_relative_to for Python <3.9 TODO: py39: Remove this backport and use stdlib function. """ try: self.relative_to(*other) return True except ValueError: return False