Skip to content

Commit b72ec6f

Browse files
authored
Merge pull request #41 from alexander255/master
Rethink the exception hierarchy
2 parents edad799 + 6f0acad commit b72ec6f

17 files changed

+331
-233
lines changed

multiaddr/codecs/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99

1010
class NoneCodec:
11-
SIZE = 0
12-
IS_PATH = False
11+
SIZE = 0
12+
IS_PATH = False
1313

1414

1515
CODEC_CACHE = {}
@@ -19,4 +19,4 @@ def codec_by_name(name):
1919
codec = CODEC_CACHE.get(name)
2020
if not codec:
2121
codec = CODEC_CACHE[name] = importlib.import_module(".{0}".format(name), __name__)
22-
return codec
22+
return codec

multiaddr/codecs/_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ def packed_net_bytes_to_int(b):
55
else: # PY2
66
def packed_net_bytes_to_int(b):
77
"""Convert the given big-endian byte-string to an int."""
8-
return int(b.encode('hex'), 16)
8+
return int(b.encode('hex'), 16)

multiaddr/codecs/fspath.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@
1717
import sys
1818

1919
def fsencode(path):
20-
if not isinstance(path, six.binary_type):
20+
if not isinstance(path, six.binary_type): # pragma: no cover
2121
path = path.encode(sys.getfilesystemencoding())
2222
return path
2323

2424
def fsdecode(path):
25-
if not isinstance(path, six.text_type):
25+
if not isinstance(path, six.text_type): # pragma: no cover
2626
path = path.decode(sys.getfilesystemencoding())
2727
return path
2828

2929

3030
def to_bytes(proto, string):
31-
return fsencode(string)
31+
return fsencode(string)
3232

3333

3434
def to_string(proto, buf):
35-
return fsdecode(buf)
35+
return fsdecode(buf)

multiaddr/codecs/idna.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111

1212
def to_bytes(proto, string):
13-
return idna.encode(string, uts46=True)
13+
return idna.encode(string, uts46=True)
1414

1515

1616
def to_string(proto, buf):
17-
return idna.decode(buf)
17+
return idna.decode(buf)

multiaddr/codecs/ip4.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111

1212

1313
def to_bytes(proto, string):
14-
try:
15-
return netaddr.IPAddress(string, version=4).packed
16-
except Exception:
17-
raise ValueError("failed to parse ip4 addr: %s" % string)
14+
return netaddr.IPAddress(string, version=4).packed
1815

1916

2017
def to_string(proto, buf):
21-
ip_addr = netaddr.IPAddress(packed_net_bytes_to_int(buf), version=4)
22-
return six.text_type(ip_addr)
18+
ip_addr = netaddr.IPAddress(packed_net_bytes_to_int(buf), version=4)
19+
return six.text_type(ip_addr)

multiaddr/codecs/ip6.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111

1212

1313
def to_bytes(proto, string):
14-
try:
15-
return netaddr.IPAddress(string, version=6).packed
16-
except Exception:
17-
raise ValueError("failed to parse ip4 addr: %s" % string)
14+
return netaddr.IPAddress(string, version=6).packed
1815

1916

2017
def to_string(proto, buf):
21-
ip_addr = netaddr.IPAddress(packed_net_bytes_to_int(buf), version=6)
22-
return six.text_type(ip_addr)
18+
ip_addr = netaddr.IPAddress(packed_net_bytes_to_int(buf), version=6)
19+
return six.text_type(ip_addr)

multiaddr/codecs/onion.py

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,31 @@
1010

1111

1212
def to_bytes(proto, string):
13-
addr = string.split(":")
14-
if len(addr) != 2:
15-
raise ValueError(
16-
"failed to parse %s addr: %s does not contain a port number."
17-
% (proto.name, string))
18-
19-
# onion address without the ".onion" substring
20-
if len(addr[0]) != 16:
21-
raise ValueError(
22-
"failed to parse %s addr: %s not a Tor onion address."
23-
% (proto.name, string))
24-
try:
25-
onion_host_bytes = base64.b32decode(addr[0].upper())
26-
except Exception as ex:
27-
raise ValueError(
28-
"failed to decode base32 %s addr: %s %s"
29-
% (proto.name, string, str(ex)))
30-
31-
# onion port number
32-
try:
33-
port = int(addr[1])
34-
except Exception as ex:
35-
raise ValueError("failed to parse %s addr: %s"
36-
% (proto.name, str(ex)))
37-
if port >= 65536:
38-
raise ValueError("failed to parse %s addr: %s"
39-
% (proto.name, "port greater than 65536"))
40-
if port < 1:
41-
raise ValueError("failed to parse %s addr: %s"
42-
% (proto.name, "port less than 1"))
43-
44-
return b''.join((onion_host_bytes, struct.pack('>H', port)))
13+
addr = string.split(":")
14+
if len(addr) != 2:
15+
raise ValueError("Does not contain a port number")
16+
17+
# onion address without the ".onion" substring
18+
if len(addr[0]) != 16:
19+
raise ValueError("Invalid onion host address length (must be 16 characters)")
20+
try:
21+
onion_host_bytes = base64.b32decode(addr[0].upper())
22+
except Exception as exc:
23+
six.raise_from(ValueError("Cannot decode {0!r} as base32: {1}".format(addr[0], exc)), exc)
24+
25+
# onion port number
26+
try:
27+
port = int(addr[1], 10)
28+
except ValueError as exc:
29+
six.raise_from(ValueError("Port number is not a base 10 integer"), exc)
30+
if port not in range(1, 65536):
31+
raise ValueError("Port number is not in range(1, 65536)")
32+
33+
return b''.join((onion_host_bytes, struct.pack('>H', port)))
4534

4635

4736
def to_string(proto, buf):
48-
addr_bytes, port_bytes = (buf[:-2], buf[-2:])
49-
addr = base64.b32encode(addr_bytes).decode('ascii').lower()
50-
port = six.text_type(struct.unpack('>H', port_bytes)[0])
51-
return u':'.join([addr, port])
37+
addr_bytes, port_bytes = (buf[:-2], buf[-2:])
38+
addr = base64.b32encode(addr_bytes).decode('ascii').lower()
39+
port = six.text_type(struct.unpack('>H', port_bytes)[0])
40+
return u':'.join([addr, port])

multiaddr/codecs/p2p.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,14 @@
1111

1212

1313
def to_bytes(proto, string):
14-
# the address is a base58-encoded string
15-
try:
16-
if six.PY2 and isinstance(string, unicode):
17-
string = string.encode("ascii")
18-
mm = base58.b58decode(string)
19-
except Exception as ex:
20-
raise ValueError("failed to parse p2p addr: %s %s" % (string, str(ex)))
21-
if len(mm) < 5:
22-
raise ValueError("invalid P2P multihash: %s" % mm)
23-
return mm
14+
# the address is a base58-encoded string
15+
if six.PY2 and isinstance(string, unicode):
16+
string = string.encode("ascii")
17+
mm = base58.b58decode(string)
18+
if len(mm) < 5:
19+
raise ValueError("P2P MultiHash too short: len() < 5")
20+
return mm
2421

2522

2623
def to_string(proto, buf):
27-
return base58.b58encode(buf).decode('ascii')
24+
return base58.b58encode(buf).decode('ascii')

multiaddr/codecs/uint16be.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,15 @@
99

1010

1111
def to_bytes(proto, string):
12-
try:
13-
return struct.pack('>H', int(string, 10))
14-
except ValueError as ex:
15-
raise ValueError("failed to parse %s addr: %s"
16-
% (proto.name, str(ex)))
17-
except struct.error:
18-
raise ValueError("failed to parse %s addr: %s" %
19-
(proto.name, "greater than 65536"))
12+
try:
13+
return struct.pack('>H', int(string, 10))
14+
except ValueError as exc:
15+
six.raise_from(ValueError("Not a base 10 integer"), exc)
16+
except struct.error as exc:
17+
six.raise_from(ValueError("Integer not in range(65536)"), exc)
2018

2119

2220
def to_string(proto, buf):
23-
if len(buf) != 2:
24-
raise ValueError("Not a uint16")
25-
return six.text_type(struct.unpack('>H', buf)[0])
21+
if len(buf) != 2:
22+
raise ValueError("Invalid integer length (must be 2 bytes / 16 bits)")
23+
return six.text_type(struct.unpack('>H', buf)[0])

multiaddr/exceptions.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
class Error(Exception):
2+
pass
3+
4+
5+
class LookupError(LookupError, Error):
6+
pass
7+
8+
9+
class ProtocolLookupError(LookupError):
10+
"""
11+
MultiAddr did not contain a protocol with the requested code
12+
"""
13+
14+
def __init__(self, proto, string):
15+
self.proto = proto
16+
self.string = string
17+
18+
super(ProtocolLookupError, self).__init__(
19+
"MultiAddr {0!r} does not contain protocol {1}".format(string, proto)
20+
)
21+
22+
23+
class ParseError(ValueError, Error):
24+
pass
25+
26+
27+
class StringParseError(ParseError):
28+
"""
29+
MultiAddr string representation could not be parsed
30+
"""
31+
32+
def __init__(self, message, string, protocol=None, original=None):
33+
self.message = message
34+
self.string = string
35+
self.protocol = protocol
36+
self.original = original
37+
38+
if protocol:
39+
message = "Invalid MultiAddr {0!r} protocol {1}: {2}".format(string, protocol, message)
40+
else:
41+
message = "Invalid MultiAddr {0!r}: {1}".format(string, message)
42+
43+
super(StringParseError, self).__init__(message)
44+
45+
46+
class BinaryParseError(ParseError):
47+
"""
48+
MultiAddr binary representation could not be parsed
49+
"""
50+
51+
def __init__(self, message, binary, protocol, original=None):
52+
self.message = message
53+
self.binary = binary
54+
self.protocol = protocol
55+
self.original = original
56+
57+
message = "Invalid binary MultiAddr protocol {0}: {1}".format(protocol, message)
58+
59+
super(BinaryParseError, self).__init__(message)
60+
61+
62+
class ProtocolManagerError(Error):
63+
pass
64+
65+
66+
class ProtocolExistsError(ProtocolManagerError):
67+
"""
68+
Protocol with the given name or code already exists
69+
"""
70+
def __init__(self, proto, kind="name"):
71+
self.proto = proto
72+
self.kind = kind
73+
74+
super(ProtocolExistsError, self).__init__(
75+
"Protocol with {0} {1!r} already exists".format(kind, getattr(proto, kind))
76+
)
77+
78+
class ProtocolNotFoundError(ProtocolManagerError):
79+
"""
80+
No protocol with the given name or code found
81+
"""
82+
def __init__(self, value, kind="name"):
83+
self.value = value
84+
self.kind = kind
85+
86+
super(ProtocolNotFoundError, self).__init__(
87+
"No protocol with {0} {1!r} found".format(kind, value)
88+
)

0 commit comments

Comments
 (0)