1212
1313"""bencode.py - bencode encoder + decoder."""
1414
15- from bencode .BTL import BTFailure
15+ from bencode .common import Bencached
16+ from bencode .decoder import BencodeDecoder
17+ from bencode .encoder import BencodeEncoder
1618from bencode .exceptions import BencodeDecodeError
1719
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-
3120try :
3221 import pathlib
3322except ImportError :
3423 pathlib = None
3524
36- PY2 = sys .version_info [0 ] == 2
37- PY3 = sys .version_info [0 ] == 3
38-
3925__all__ = (
40- 'BTFailure' ,
26+ 'Bencached' ,
27+ 'Bencode' ,
28+ 'BencodeDecoder' ,
4129 'BencodeDecodeError' ,
30+ 'BencodeEncoder' ,
4231 'bencode' ,
4332 'bdecode' ,
4433 'bread' ,
4837)
4938
5039
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 ()
7144
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 )
7447
75- colon += 1
76- s = x [ colon : colon + n ]
48+ def encode ( self , value ):
49+ return self . encoder . encode ( value )
7750
78- return bytes (s ), colon + n
7951
52+ DEFAULT = Bencode ()
8053
81- def decode_list (x , f ):
82- # type: (bytes, int) -> Tuple[List, int]
83- r , f = [], f + 1
8454
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
10657 """
58+ Encode ``value`` into the bencode format.
10759
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
11962
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 )
13567
13668
13769def bdecode (value ):
@@ -145,160 +77,7 @@ def bdecode(value):
14577 :return: Decoded value
14678 :rtype: object
14779 """
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 )
30281
30382
30483def bread (fd ):
@@ -337,3 +116,8 @@ def bwrite(data, # type: Union[Tuple, List, OrderedDict, Dict, bool, int, str,
337116 fd .write (bencode (data ))
338117 else :
339118 fd .write (bencode (data ))
119+
120+
121+ # Compatibility Proxies
122+ encode = bencode
123+ decode = bdecode
0 commit comments