12
12
13
13
"""bencode.py - bencode encoder + decoder."""
14
14
15
- from bencode .BTL import BTFailure
15
+ from bencode .common import Bencached
16
+ from bencode .decoder import BencodeDecoder
17
+ from bencode .encoder import BencodeEncoder
16
18
from bencode .exceptions import BencodeDecodeError
17
19
18
- from collections import deque
19
- import sys
20
-
21
- try :
22
- from typing import Dict , List , Tuple , Deque , Union , TextIO , BinaryIO , Any
23
- except ImportError :
24
- Dict = List = Tuple = Deque = Union = TextIO = BinaryIO = Any = None
25
-
26
- try :
27
- from collections import OrderedDict
28
- except ImportError :
29
- OrderedDict = None
30
-
31
20
try :
32
21
import pathlib
33
22
except ImportError :
34
23
pathlib = None
35
24
36
- PY2 = sys .version_info [0 ] == 2
37
- PY3 = sys .version_info [0 ] == 3
38
-
39
25
__all__ = (
40
- 'BTFailure' ,
26
+ 'Bencached' ,
27
+ 'Bencode' ,
28
+ 'BencodeDecoder' ,
41
29
'BencodeDecodeError' ,
30
+ 'BencodeEncoder' ,
42
31
'bencode' ,
43
32
'bdecode' ,
44
33
'bread' ,
48
37
)
49
38
50
39
51
- def decode_int (x , f ):
52
- # type: (bytes, int) -> Tuple[int, int]
53
- f += 1
54
- newf = x .index (b'e' , f )
55
- n = int (x [f :newf ])
56
-
57
- if x [f :f + 1 ] == b'-' :
58
- if x [f + 1 :f + 2 ] == b'0' :
59
- raise ValueError
60
- elif x [f :f + 1 ] == b'0' and newf != f + 1 :
61
- raise ValueError
62
-
63
- return n , newf + 1
64
-
65
-
66
- def decode_string (x , f ):
67
- # type: (bytes, int) -> Tuple[bytes, int]
68
- """Decode torrent bencoded 'string' in x starting at f."""
69
- colon = x .index (b':' , f )
70
- n = int (x [f :colon ])
40
+ class Bencode (object ):
41
+ def __init__ (self ):
42
+ self .decoder = BencodeDecoder ()
43
+ self .encoder = BencodeEncoder ()
71
44
72
- if x [ f : f + 1 ] == b'0' and colon != f + 1 :
73
- raise ValueError
45
+ def decode ( self , value ) :
46
+ return self . decoder . decode ( value )
74
47
75
- colon += 1
76
- s = x [ colon : colon + n ]
48
+ def encode ( self , value ):
49
+ return self . encoder . encode ( value )
77
50
78
- return bytes (s ), colon + n
79
51
52
+ DEFAULT = Bencode ()
80
53
81
- def decode_list (x , f ):
82
- # type: (bytes, int) -> Tuple[List, int]
83
- r , f = [], f + 1
84
54
85
- while x [f :f + 1 ] != b'e' :
86
- v , f = decode_func [x [f :f + 1 ]](x , f )
87
- r .append (v )
88
-
89
- return r , f + 1
90
-
91
-
92
- def decode_dict (x , f , force_sort = True ):
93
- # type: (bytes, int, bool) -> Tuple[OrderedDict[str, Any], int]
94
- """Decode bencoded data to an OrderedDict.
95
-
96
- The BitTorrent standard states that:
97
- Keys must be strings and appear in sorted order (sorted as raw
98
- strings, not alphanumerics)
99
- - http://www.bittorrent.org/beps/bep_0003.html
100
-
101
- Therefore, this function will force the keys to be strings (decoded
102
- from utf-8), and by default the keys are (re)sorted after reading.
103
- Set force_sort to False to keep the order of the dictionary as
104
- represented in x, as many other encoders and decoders do not force this
105
- property.
55
+ def bencode (value ):
56
+ # type: (Union[Tuple, List, OrderedDict, Dict, bool, int, str, bytes]) -> bytes
106
57
"""
58
+ Encode ``value`` into the bencode format.
107
59
108
- r , f = OrderedDict (), f + 1
109
-
110
- while x [f :f + 1 ] != b'e' :
111
- k , f = decode_string (x , f )
112
- r [k ], f = decode_func [x [f :f + 1 ]](x , f )
113
-
114
- if force_sort :
115
- r = OrderedDict (sorted (r .items ()))
116
-
117
- return r , f + 1
118
-
60
+ :param value: Value
61
+ :type value: object
119
62
120
- # noinspection PyDictCreation
121
- decode_func = {}
122
- decode_func [b'l' ] = decode_list
123
- decode_func [b'i' ] = decode_int
124
- decode_func [b'0' ] = decode_string
125
- decode_func [b'1' ] = decode_string
126
- decode_func [b'2' ] = decode_string
127
- decode_func [b'3' ] = decode_string
128
- decode_func [b'4' ] = decode_string
129
- decode_func [b'5' ] = decode_string
130
- decode_func [b'6' ] = decode_string
131
- decode_func [b'7' ] = decode_string
132
- decode_func [b'8' ] = decode_string
133
- decode_func [b'9' ] = decode_string
134
- decode_func [b'd' ] = decode_dict
63
+ :return: Bencode formatted string
64
+ :rtype: str
65
+ """
66
+ return DEFAULT .encode (value )
135
67
136
68
137
69
def bdecode (value ):
@@ -145,160 +77,7 @@ def bdecode(value):
145
77
:return: Decoded value
146
78
:rtype: object
147
79
"""
148
- try :
149
- value = to_binary (value )
150
- data , length = decode_func [value [0 :1 ]](value , 0 )
151
- except (IndexError , KeyError , TypeError , ValueError ):
152
- raise BencodeDecodeError ("not a valid bencoded string" )
153
-
154
- if length != len (value ):
155
- raise BencodeDecodeError ("invalid bencoded value (data after valid prefix)" )
156
-
157
- return data
158
-
159
-
160
- class Bencached (object ):
161
- __slots__ = ['bencoded' ]
162
-
163
- def __init__ (self , s ):
164
- self .bencoded = s
165
-
166
-
167
- def encode_bencached (x , r ):
168
- # type: (Bencached, Deque[bytes]) -> None
169
- r .append (x .bencoded )
170
-
171
-
172
- def encode_int (x , r ):
173
- # type: (int, Deque[bytes]) -> None
174
- r .extend ((b'i' , str (x ).encode ('utf-8' ), b'e' ))
175
-
176
-
177
- def encode_bool (x , r ):
178
- # type: (bool, Deque[bytes]) -> None
179
- if x :
180
- encode_int (1 , r )
181
- else :
182
- encode_int (0 , r )
183
-
184
-
185
- def encode_bytes (x , r ):
186
- # type: (bytes, Deque[bytes]) -> None
187
- r .extend ((str (len (x )).encode ('utf-8' ), b':' , x ))
188
-
189
-
190
- def encode_string (x , r ):
191
- # type: (str, Deque[bytes]) -> None
192
- return encode_bytes (x .encode ("UTF-8" ), r )
193
-
194
-
195
- def encode_list (x , r ):
196
- # type: (List, Deque[bytes]) -> None
197
- r .append (b'l' )
198
-
199
- for i in x :
200
- encode_func [type (i )](i , r )
201
-
202
- r .append (b'e' )
203
-
204
-
205
- def encode_dict (x , r ):
206
- # type: (Dict, Deque[bytes]) -> None
207
- r .append (b'd' )
208
-
209
- # force all keys to bytes, because str and bytes are incomparable
210
- ilist = [(to_binary (k ), v ) for k , v in x .items ()]
211
- ilist .sort (key = lambda kv : kv [0 ])
212
-
213
- for k , v in ilist :
214
- encode_func [type (k )](k , r )
215
- encode_func [type (v )](v , r )
216
-
217
- r .append (b'e' )
218
-
219
-
220
- def is_binary (s ):
221
- if PY3 :
222
- return isinstance (s , bytes )
223
-
224
- return isinstance (s , str )
225
-
226
-
227
- def is_text (s ):
228
- if PY3 :
229
- return isinstance (s , str )
230
-
231
- return isinstance (s , unicode ) # noqa: F821
232
-
233
-
234
- def to_binary (s ):
235
- if is_binary (s ):
236
- return s
237
-
238
- if is_text (s ):
239
- return s .encode ('utf-8' , 'strict' )
240
-
241
- raise TypeError ("expected binary or text (found %s)" % type (s ))
242
-
243
-
244
- # noinspection PyDictCreation
245
- encode_func = {}
246
- encode_func [Bencached ] = encode_bencached
247
-
248
- if PY2 :
249
- from types import DictType , IntType , ListType , LongType , StringType , TupleType , UnicodeType
250
-
251
- encode_func [DictType ] = encode_dict
252
- encode_func [IntType ] = encode_int
253
- encode_func [ListType ] = encode_list
254
- encode_func [LongType ] = encode_int
255
- encode_func [StringType ] = encode_bytes
256
- encode_func [TupleType ] = encode_list
257
- encode_func [UnicodeType ] = encode_string
258
-
259
- if OrderedDict is not None :
260
- encode_func [OrderedDict ] = encode_dict
261
-
262
- try :
263
- from types import BooleanType
264
-
265
- encode_func [BooleanType ] = encode_bool
266
- except ImportError :
267
- pass
268
- else :
269
- encode_func [OrderedDict ] = encode_dict
270
- encode_func [bool ] = encode_bool
271
- encode_func [dict ] = encode_dict
272
- encode_func [int ] = encode_int
273
- encode_func [list ] = encode_list
274
- encode_func [str ] = encode_string
275
- encode_func [tuple ] = encode_list
276
- encode_func [bytes ] = encode_bytes
277
-
278
-
279
- def bencode (value ):
280
- # type: (Union[Tuple, List, OrderedDict, Dict, bool, int, str, bytes]) -> bytes
281
- """
282
- Encode ``value`` into the bencode format.
283
-
284
- :param value: Value
285
- :type value: object
286
-
287
- :return: Bencode formatted string
288
- :rtype: str
289
- """
290
- r = deque () # makes more sense for something with lots of appends
291
-
292
- # Encode provided value
293
- encode_func [type (value )](value , r )
294
-
295
- # Join parts
296
- return b'' .join (r )
297
-
298
-
299
- # Method proxies (for compatibility with other libraries)
300
- decode = bdecode
301
- encode = bencode
80
+ return DEFAULT .decode (value )
302
81
303
82
304
83
def bread (fd ):
@@ -337,3 +116,8 @@ def bwrite(data, # type: Union[Tuple, List, OrderedDict, Dict, bool, int, str,
337
116
fd .write (bencode (data ))
338
117
else :
339
118
fd .write (bencode (data ))
119
+
120
+
121
+ # Compatibility Proxies
122
+ encode = bencode
123
+ decode = bdecode
0 commit comments