Skip to content

Commit d38d9fd

Browse files
authored
Merge pull request #102 from nexB/101-univers-support-for-conan
Add ConanVersionRange class and test
2 parents 4078ef2 + 01ff53e commit d38d9fd

23 files changed

Lines changed: 4146 additions & 0 deletions

src/univers/conan/__init__.py

Whitespace-only changes.

src/univers/conan/errors.py

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#
2+
# Copyright (c) 2019 JFrog LTD
3+
# SPDX-License-Identifier: MIT
4+
#
5+
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
6+
7+
"""
8+
Exceptions raised and handled in Conan
9+
These exceptions are mapped between server (as an HTTP response) and client
10+
through the REST API. When an error happens in server its translated to an HTTP
11+
error code that its sent to client. Client reads the server code and raise the
12+
matching exception.
13+
14+
see return_plugin.py
15+
16+
"""
17+
from contextlib import contextmanager
18+
19+
20+
@contextmanager
21+
def conanfile_remove_attr(conanfile, names, method):
22+
"""remove some self.xxxx attribute from the class, so it raises an exception if used
23+
within a given conanfile method
24+
"""
25+
original_class = type(conanfile)
26+
27+
def _prop(attr_name):
28+
def _m(_):
29+
raise ConanException(f"'self.{attr_name}' access in '{method}()' method is forbidden")
30+
31+
return property(_m)
32+
33+
try:
34+
new_class = type(original_class.__name__, (original_class,), {})
35+
conanfile.__class__ = new_class
36+
for name in names:
37+
setattr(new_class, name, _prop(name))
38+
yield
39+
finally:
40+
conanfile.__class__ = original_class
41+
42+
43+
@contextmanager
44+
def conanfile_exception_formatter(conanfile_name, func_name):
45+
"""
46+
Decorator to throw an exception formatted with the line of the conanfile where the error ocurrs.
47+
"""
48+
49+
def _raise_conanfile_exc(e):
50+
m = _format_conanfile_exception(conanfile_name, func_name, e)
51+
raise ConanExceptionInUserConanfileMethod(m)
52+
53+
try:
54+
yield
55+
# TODO: Move ConanInvalidConfiguration from here?
56+
except ConanInvalidConfiguration as exc:
57+
msg = "{}: Invalid configuration: {}".format(str(conanfile_name), exc)
58+
raise ConanInvalidConfiguration(msg)
59+
except AttributeError as exc:
60+
list_methods = [m for m in dir(list) if not m.startswith("__")]
61+
if (
62+
"NoneType" in str(exc)
63+
and func_name in ["layout", "package_info"]
64+
and any(method in str(exc) for method in list_methods)
65+
):
66+
raise ConanException(
67+
"{}: {}. No default values are set for components. You are probably "
68+
"trying to manipulate a component attribute in the '{}' method "
69+
"without defining it previously".format(str(conanfile_name), exc, func_name)
70+
)
71+
else:
72+
_raise_conanfile_exc(exc)
73+
except Exception as exc:
74+
_raise_conanfile_exc(exc)
75+
76+
77+
def _format_conanfile_exception(scope, method, exception):
78+
"""
79+
It will iterate the traceback lines, when it finds that the source code is inside the users
80+
conanfile it "start recording" the messages, when the trace exits the conanfile we return
81+
the traces.
82+
"""
83+
import sys
84+
import traceback
85+
86+
try:
87+
conanfile_reached = False
88+
tb = sys.exc_info()[2]
89+
index = 0
90+
content_lines = []
91+
92+
while True: # If out of index will raise and will be captured later
93+
# 40 levels of nested functions max, get the latest
94+
filepath, line, name, contents = traceback.extract_tb(tb, 40)[index]
95+
if "conanfile.py" not in filepath: # Avoid show trace from internal conan source code
96+
if conanfile_reached: # The error goes to internal code, exit print
97+
break
98+
else:
99+
if not conanfile_reached: # First line
100+
msg = "%s: Error in %s() method" % (scope, method)
101+
msg += ", line %d\n\t%s" % (line, contents)
102+
else:
103+
msg = (
104+
"while calling '%s', line %d\n\t%s" % (name, line, contents)
105+
if line
106+
else "\n\t%s" % contents
107+
)
108+
content_lines.append(msg)
109+
conanfile_reached = True
110+
index += 1
111+
except Exception:
112+
pass
113+
ret = "\n".join(content_lines)
114+
ret += "\n\t%s: %s" % (exception.__class__.__name__, str(exception))
115+
return ret
116+
117+
118+
class ConanException(Exception):
119+
"""
120+
Generic conans exception
121+
"""
122+
123+
def __init__(self, *args, **kwargs):
124+
self.info = None
125+
self.remote = kwargs.pop("remote", None)
126+
super(ConanException, self).__init__(*args, **kwargs)
127+
128+
def remote_message(self):
129+
if self.remote:
130+
return " [Remote: {}]".format(self.remote.name)
131+
return ""
132+
133+
def __str__(self):
134+
135+
msg = super(ConanException, self).__str__()
136+
137+
try:
138+
msg = str(msg)
139+
except Exception:
140+
msg = repr(msg)
141+
142+
if self.remote:
143+
return "{}.{}".format(msg, self.remote_message())
144+
145+
return msg
146+
147+
148+
class ConanReferenceDoesNotExistInDB(ConanException):
149+
"""Reference does not exist in cache db"""
150+
151+
pass
152+
153+
154+
class ConanReferenceAlreadyExistsInDB(ConanException):
155+
"""Reference already exists in cache db"""
156+
157+
pass
158+
159+
160+
class NoRemoteAvailable(ConanException):
161+
"""No default remote configured or the specified remote do not exists"""
162+
163+
pass
164+
165+
166+
class InvalidNameException(ConanException):
167+
pass
168+
169+
170+
class ConanConnectionError(ConanException):
171+
pass
172+
173+
174+
class ConanOutdatedClient(ConanException):
175+
pass
176+
177+
178+
class ConanExceptionInUserConanfileMethod(ConanException):
179+
pass
180+
181+
182+
class ConanInvalidConfiguration(ConanExceptionInUserConanfileMethod):
183+
"""
184+
This binary, for the requested configuration and package-id cannot be built
185+
"""
186+
187+
pass
188+
189+
190+
class ConanMigrationError(ConanException):
191+
pass
192+
193+
194+
# Remote exceptions #
195+
class InternalErrorException(ConanException):
196+
"""
197+
Generic 500 error
198+
"""
199+
200+
pass
201+
202+
203+
class RequestErrorException(ConanException):
204+
"""
205+
Generic 400 error
206+
"""
207+
208+
pass
209+
210+
211+
class AuthenticationException(ConanException): # 401
212+
"""
213+
401 error
214+
"""
215+
216+
pass
217+
218+
219+
class ForbiddenException(ConanException): # 403
220+
"""
221+
403 error
222+
"""
223+
224+
pass
225+
226+
227+
class NotFoundException(ConanException): # 404
228+
"""
229+
404 error
230+
"""
231+
232+
def __init__(self, *args, **kwargs):
233+
self.remote = kwargs.pop("remote", None)
234+
super(NotFoundException, self).__init__(*args, **kwargs)
235+
236+
237+
class UserInterfaceErrorException(RequestErrorException):
238+
"""
239+
420 error
240+
"""
241+
242+
pass
243+
244+
245+
EXCEPTION_CODE_MAPPING = {
246+
InternalErrorException: 500,
247+
RequestErrorException: 400,
248+
AuthenticationException: 401,
249+
ForbiddenException: 403,
250+
NotFoundException: 404,
251+
UserInterfaceErrorException: 420,
252+
}

src/univers/conan/errors.py.ABOUT

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
about_resource: errors.py
2+
package_url: pkg:pypi/conan@2.0.0
3+
copyright: |
4+
Copyright (c) 2019 JFrog LTD
5+
download_url: https://github.com/conan-io/conan/blob/release/2.0/conans/errors.py
6+
license_expression: MIT
7+
homepage_url: https://github.com/conan-io/conan
8+
notice_file: errors.py.NOTICE

src/univers/conan/errors.py.NOTICE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2019 JFrog LTD
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

0 commit comments

Comments
 (0)