• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SwissDataScienceCenter / renku-python / 7242391285

18 Dec 2023 01:23AM UTC coverage: 61.432% (-21.3%) from 82.732%
7242391285

Pull #3671

github

RenkuBot
chore: release v2.8.1
Pull Request #3671: chore: release v2.8.1

104 of 137 new or added lines in 31 files covered. (75.91%)

6464 existing lines in 180 files now uncovered.

18537 of 30175 relevant lines covered (61.43%)

1.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.74
/renku/ui/cli/exception_handler.py
1
# Copyright Swiss Data Science Center (SDSC). A partnership between
2
# École Polytechnique Fédérale de Lausanne (EPFL) and
3
# Eidgenössische Technische Hochschule Zürich (ETHZ).
4
#
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
8
#
9
#     http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16
"""Renku is not bug-free and you can help us to find them.
3✔
17

18
GitHub
19
~~~~~~
20

21
You can quickly open an issue on GitHub with a traceback and minimal system
22
information when you hit an unhandled exception in the CLI.
23

24
.. code-block:: text
25

26
    Ahhhhhhhh! You have found a bug. 🐞
27

28
    1. Open an issue by typing "open";
29
    2. Print human-readable information by typing "print";
30
    3. See the full traceback without submitting details (default: "ignore").
31

32
    Please select an action by typing its name (open, print, ignore) [ignore]:
33

34
Sentry
35
~~~~~~
36

37
When using ``renku`` as a hosted service the Sentry integration can be enabled
38
to help developers iterate faster by showing them where bugs happen, how often,
39
and who is affected.
40

41
1. Install ``Sentry-SDK`` with ``python -m pip install sentry-sdk``;
42
2. Set environment variables ``SENTRY_DSN=true`` and
43
   ``SENTRY_DSN=https://<key>@sentry.<domain>/<project>``.
44
3. Set the environment variable ``SENTRY_SAMPLE_RATE=0.2``. This would track
45
   20% of all requests in Sentry performance monitoring. Set to 0 to disable.
46

47
.. warning:: User information might be sent to help resolving the problem.
48
   If you are not using your own Sentry instance you should inform users
49
   that you are sending possibly sensitive information to a 3rd-party service.
50
"""
51

52
import os
3✔
53
import platform
3✔
54
import re
3✔
55
import sys
3✔
56
import textwrap
3✔
57
import traceback
3✔
58
from urllib.parse import urlencode
3✔
59

60
import click
3✔
61

62
import renku.ui.cli.utils.color as color
3✔
63
from renku.command.util import ERROR
3✔
64
from renku.core import errors
3✔
65
from renku.ui.service.config import SENTRY_ENABLED, SENTRY_SAMPLERATE
3✔
66

67
_BUG = click.style("Ahhhhhhhh! You have found a bug. 🐞\n\n", fg=color.RED, bold=True)
3✔
68
HAS_SENTRY = SENTRY_ENABLED
3✔
69

70
if SENTRY_ENABLED:
3✔
71
    try:
×
72
        from importlib.metadata import PackageNotFoundError, distribution
×
73
    except ImportError:
×
74
        from importlib_metadata import PackageNotFoundError, distribution  # type: ignore
×
75

76
    try:
×
77
        distribution("sentry-sdk")
×
78
    except PackageNotFoundError:
×
79
        HAS_SENTRY = False
×
80

81

82
class RenkuExceptionsHandler(click.Group):
3✔
83
    """Handles all RenkuExceptions."""
84

85
    def main(self, *args, **kwargs):
3✔
86
        """Catch and print all Renku exceptions."""
87
        from renku.core.errors import MigrationRequired, ParameterError, ProjectNotSupported, RenkuException, UsageError
2✔
88

89
        try:
2✔
90
            return super().main(*args, **kwargs)
2✔
91
        except errors.LockError:
2✔
UNCOV
92
            click.echo(
×
93
                click.style("Unable to acquire lock.\n", fg=color.RED)
94
                + "Hint: Please wait for another renku process to finish and then try again."
95
            )
96
        except RenkuException as e:
2✔
97
            click.echo(ERROR + str(e), err=True)
1✔
98
            if e.__cause__ is not None:
1✔
UNCOV
99
                click.echo(f"\n{traceback.format_exc()}")
×
100
            exit_code = 1
1✔
101
            if isinstance(e, (ParameterError, UsageError)):
1✔
102
                exit_code = 2
1✔
103
            elif isinstance(e, MigrationRequired):
1✔
UNCOV
104
                exit_code = 3
×
105
            elif isinstance(e, ProjectNotSupported):
1✔
UNCOV
106
                exit_code = 4
×
107
            sys.exit(exit_code)
1✔
108

109

110
class IssueFromTraceback(RenkuExceptionsHandler):
3✔
111
    """Create an issue with formatted exception."""
112

113
    REPO_URL = "https://github.com/SwissDataScienceCenter/renku-python"
3✔
114

115
    ISSUE_SUFFIX = "/issues/new"
3✔
116

117
    def __init__(self, *args, **kwargs):
3✔
118
        """Initialize a Sentry client."""
119
        super().__init__(*args, **kwargs)
3✔
120

121
        if HAS_SENTRY:
3✔
122
            import sentry_sdk
×
123

124
            sentry_sdk.init(
×
125
                dsn=os.getenv("SENTRY_DSN"), environment=os.getenv("SENTRY_ENV"), traces_sample_rate=SENTRY_SAMPLERATE
126
            )
127

128
    def main(self, *args, **kwargs):
3✔
129
        """Catch all exceptions."""
130
        try:
2✔
131
            return super().main(*args, **kwargs)
2✔
132
        except Exception:
2✔
133
            if HAS_SENTRY:
1✔
134
                self._handle_sentry()
×
135

136
            if not (sys.stdin.isatty() and sys.stdout.isatty()):
1✔
137
                raise
1✔
138

139
            self._handle_github()
×
140

141
    def _handle_sentry(self):
3✔
142
        """Handle exceptions using Sentry."""
143
        from sentry_sdk import capture_exception, configure_scope
×
144
        from sentry_sdk.utils import capture_internal_exceptions
×
145

146
        with configure_scope() as scope:
×
147
            with capture_internal_exceptions():
×
148
                from renku.core.util.git import get_git_repository
×
149

150
                user = get_git_repository().get_user()
×
151

152
                scope.user = {"name": user.name, "email": user.email}
×
153

154
            event_id = capture_exception()
×
155
            click.echo(_BUG + f"Recorded in Sentry with ID: {event_id}\n", err=True)
×
156
            raise
×
157

158
    def _handle_github(self):
3✔
159
        """Handle exception and submit it as GitHub issue."""
160
        value = click.prompt(
×
161
            _BUG
162
            + click.style('1. Open an issue by typing "open";\n', fg=color.GREEN)
163
            + click.style("2. Print human-readable information by typing " '"print";\n', fg=color.YELLOW)
164
            + click.style(
165
                "3. See the full traceback without submitting details " '(default: "ignore").\n\n', fg=color.RED
166
            )
167
            + "Please select an action by typing its name",
168
            type=click.Choice(["open", "print", "ignore"]),
169
            default="ignore",
170
        )
171
        getattr(self, "_process_" + value)()
×
172

173
    def _format_issue_title(self):
3✔
174
        """Return formatted title."""
175
        return textwrap.shorten("cli: renku " + " ".join(sys.argv[1:]), width=50)
×
176

177
    def _format_issue_body(self, limit=-5):
3✔
178
        """Return formatted body."""
179
        from renku import __version__
×
180

181
        re_paths = r"(" + r"|".join([path or os.getcwd() for path in sys.path]) + r")"
×
182
        tb = re.sub(re_paths, "[...]", traceback.format_exc(limit=limit))
×
183

184
        return (
×
185
            "## Describe the bug\nA clear and concise description.\n\n"
186
            "## Details\n"
187
            "*Please verify and redact the details.*\n\n"
188
            "**Renku version:** " + __version__ + "\n"
189
            "**OS:** " + platform.system() + " (" + platform.version() + ")\n"
190
            "**Python:** " + platform.python_version() + "\n\n"
191
            "### Traceback\n\n```\n" + tb + "```\n\n"
192
            "## Additional context\nAdd any other context about the problem."
193
        )
194

195
    def _format_issue_url(self):
3✔
196
        """Format full issue URL."""
197
        query = urlencode({"title": self._format_issue_title(), "body": self._format_issue_body()})
×
198
        return self.REPO_URL + self.ISSUE_SUFFIX + "?" + query
×
199

200
    def _process_open(self):
3✔
201
        """Open link in a browser."""
202
        click.launch(self._format_issue_url())
×
203
        if not click.confirm("Did it work?", default=True):
×
204
            click.echo()
×
205
            self._process_print()
×
206
            click.secho("\nOpen the line manually and copy the text above\n", fg=color.YELLOW)
×
207
            click.secho("  " + self.REPO_URL + self.ISSUE_SUFFIX + "\n", bold=True)
×
208

209
    def _process_print(self):
3✔
210
        """Print link in a console."""
211
        click.echo(self._format_issue_body(limit=None))
×
212

213
    def _process_ignore(self):
3✔
214
        """Print original exception in a console."""
215
        raise
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc