Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/dev/sagedev_wrapper.py
8815 views
1
r"""
2
Wrapper for SageDev
3
4
This module provides a wrapper for :class:`sagedev.SageDev` to produce better error
5
handling.
6
7
AUTHORS:
8
9
- Julian Rueth: initial version
10
"""
11
#*****************************************************************************
12
# Copyright (C) 2013 Julian Rueth <[email protected]>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
# as published by the Free Software Foundation; either version 2 of
16
# the License, or (at your option) any later version.
17
# http://www.gnu.org/licenses/
18
#*****************************************************************************
19
20
obsolete_commands = {
21
"add_comment": "comment",
22
"switch_branch": "checkout",
23
"download": "pull",
24
"upload": "push",
25
"switch_ticket": "checkout",
26
"set_needs_work": "needs_work",
27
"set_needs_review": "needs_review",
28
"set_needs_info": "needs_info",
29
"set_positive_review": "positive_review",
30
"reset_to_clean_working_directory" : "clean",
31
"local_tickets" : "tickets",
32
"prune_closed_tickets" : "prune_tickets",
33
}
34
35
class SageDevWrapper(object):
36
r"""
37
Wrap a :class:`sagedev.SageDev` and its public methods.
38
39
The need for this wrapper arises from the following problem:
40
Some methods of :class:`sagedev.SageDev` call other public methods of
41
:class:`sagedev.SageDev`, for example,
42
:meth:`sagedev.SageDev.checkout` relies on
43
:meth:`sagedev.SageDev.pull` in some cases. If an error occurs in
44
:meth:`sagedev.SageDev.pull` such as an
45
:class:`user_interface.OperationCancelledError`,
46
:meth:`sagedev.SageDev.pull` must reraise that error so that
47
:meth:`sagedev.SageDev.checkout` can do the necessary cleanup.
48
However, if the user called :meth:`sagedev.SageDev.pull` directly, then
49
the error should not be reraised: the user opted to cancel this operation
50
and should not be bothered with an exception message. This wrapper takes
51
care of getting this right.
52
53
INPUT:
54
55
- ``sagedev`` -- a :class:`sagedev.SageDev`
56
57
EXAMPLES::
58
59
sage: dev
60
SageDev()
61
62
TESTS::
63
64
sage: from sage.dev.test.trac_server import DoctestTracServer
65
sage: from sage.dev.test.sagedev import DoctestSageDevWrapper
66
sage: from sage.dev.test.config import DoctestConfig
67
sage: server = DoctestTracServer()
68
sage: config = DoctestConfig()
69
sage: config['trac']['password'] = 'secret'
70
sage: dev = DoctestSageDevWrapper(config, server)
71
sage: UI = dev._UI
72
sage: dev._pull_master_branch()
73
sage: dev._chdir()
74
75
``create_ticket`` silently fails for a wrapper::
76
77
sage: UI.append("# abort")
78
sage: dev.create_ticket()
79
80
Without the wrapper an exception is raised::
81
82
sage: UI.append("# abort")
83
sage: dev._sagedev.create_ticket()
84
Traceback (most recent call last):
85
...
86
OperationCancelledError: ticket edit aborted
87
"""
88
def __init__(self, sagedev):
89
r"""
90
Initialization.
91
92
TESTS::
93
94
sage: type(dev._get_object())
95
<class 'sage.dev.test.sagedev.DoctestSageDevWrapper'>
96
"""
97
self._sagedev = sagedev
98
99
self._wrap("abandon")
100
self._wrap("comment")
101
self._wrap("commit")
102
self._wrap("create_ticket")
103
self._wrap("diff")
104
self._wrap("pull")
105
self._wrap("download_patch")
106
self._wrap("edit_ticket")
107
self._wrap("import_patch")
108
self._wrap("tickets")
109
self._wrap("merge")
110
self._wrap("prune_tickets")
111
self._wrap("remote_status")
112
self._wrap("clean")
113
self._wrap("set_remote")
114
self._wrap("show_dependencies")
115
self._wrap("checkout")
116
self._wrap("push")
117
self._wrap("upload_ssh_key")
118
self._wrap("vanilla")
119
self._wrap("needs_work")
120
self._wrap("needs_review")
121
self._wrap("needs_info")
122
self._wrap("positive_review")
123
124
for old_command,new_command in obsolete_commands.items():
125
self._obsolete(old_command, new_command)
126
127
self.git = sagedev.git
128
self.trac = sagedev.trac
129
130
def _obsolete(self, old_method, new_method):
131
r"""
132
Create a method `old_method` which tells the user that `old_method`
133
does not exist anymore and has been replaced by `new_method`.
134
135
EXAMPLES::
136
137
sage: dev.obsolete
138
Traceback (most recent call last):
139
...
140
AttributeError: 'DoctestSageDevWrapper' object has no attribute 'obsolete'
141
sage: dev._obsolete("obsolete", "not_obsolete")
142
sage: dev.obsolete
143
<function wrapped at 0x...>
144
"""
145
def wrap():
146
from sage.misc.decorators import sage_wraps
147
doc = 'The command "{0}" does not exist anymore. Please use "{1}" instead.'.format(
148
self._sagedev._format_command(old_method), self._sagedev._format_command(new_method))
149
def wrapped(*args, **kwargs):
150
self._sagedev._UI.error(doc)
151
wrapped.__doc__ = doc
152
return wrapped
153
154
setattr(self, old_method, wrap())
155
156
def _wrap(self, method):
157
r"""
158
Create and register a wrapper for ``method``.
159
160
EXAMPLES::
161
162
sage: dev._local_branch_for_ticket
163
Traceback (most recent call last):
164
...
165
AttributeError: 'DoctestSageDevWrapper' object has no attribute '_local_branch_for_ticket'
166
sage: dev._wrap("_local_branch_for_ticket")
167
sage: dev._local_branch_for_ticket
168
<function wrapped at 0x...>
169
"""
170
from user_interface_error import OperationCancelledError
171
from git_error import GitError, DetachedHeadError, InvalidStateError
172
from trac_error import TracConnectionError
173
from sagedev import SageDevValueError
174
from user_interface import NORMAL, INFO, DEBUG
175
UI = self._sagedev._UI
176
def wrap(f):
177
from sage.misc.decorators import sage_wraps
178
@sage_wraps(f)
179
def wrapped(*args, **kwargs):
180
log_level = int(UI._config.get("log_level", str(NORMAL)))
181
try:
182
return f(*args, **kwargs)
183
except OperationCancelledError:
184
if log_level >= DEBUG:
185
raise
186
except GitError as e:
187
INFO_LEVEL = INFO if e.explain else NORMAL # show more info if the error was unexpected
188
189
UI.error("GitError: git exited with a non-zero exit code ({0}).", e.exit_code)
190
UI.show('This happened while executing "{0}".', e.cmd)
191
UI.debug("I tried my best to put your working tree and repository back"
192
" to its original state.")
193
if e.explain:
194
UI.error(e.explain)
195
if e.advice:
196
UI.info(e.advice)
197
if e.stdout is None:
198
pass
199
elif e.stdout.strip() == "":
200
UI.error("git printed nothing to STDOUT.")
201
else:
202
UI.error("git printed the following to STDOUT:\n{0}", e.stdout)
203
if e.stderr is None:
204
pass
205
elif e.stderr.strip() == "":
206
UI.error("git printed nothing to STDERR.")
207
else:
208
UI.error("git printed the following to STDERR:\n{0}", e.stderr)
209
if log_level >= DEBUG:
210
raise
211
except DetachedHeadError as e:
212
UI.error("Unexpectedly your repository was found to be in a"
213
" detached head state. This is probably a bug in sagedev.")
214
UI.info("Use {0} and {1} to restore your repository to a clean state.",
215
self._sagedev._format_command("clean"),
216
self._sagedev._format_command("checkout", branch="master"))
217
raise
218
except InvalidStateError as e:
219
UI.error("Unexpectedly your repository was found to be in a"
220
" non-clean state. This is probably a bug in sagedev.")
221
UI.info(['', '(use "{0}" to restore your repository to a clean state)'],
222
self._sagedev._format_command("clean"))
223
raise
224
except TracConnectionError as e:
225
UI.error("Your command failed because no connection to trac could be established.")
226
if log_level >= DEBUG:
227
raise
228
except SageDevValueError as e:
229
e.show_error(UI)
230
e.show_info(UI)
231
if log_level >= DEBUG:
232
raise
233
return wrapped
234
235
setattr(self, method, wrap(getattr(self._sagedev, method)))
236
237
def __repr__(self):
238
r"""
239
Return a printable representation of this object.
240
241
TESTS::
242
243
sage: repr(dev)
244
'SageDev()'
245
"""
246
return repr(self._sagedev)
247
248
249