Skip to content

Commit 205e6ac

Browse files
committed
Implemented backwards-compatible bencode package
The library now includes a backwards-compatible `bencode` package, with the updated interface now available on the `bencodepy` package. #16
1 parent 2547603 commit 205e6ac

21 files changed

+701
-138
lines changed

README.rst

+151-10
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,171 @@ Usage
2222

2323
.. code-block:: python
2424
25-
import bencode
25+
>>> import bencodepy
2626
27-
bencode.encode({'title': 'Example'})
28-
# 'd5:title7:Examplee'
27+
>>> bencodepy.encode({'title': 'Example'})
28+
b'd5:title7:Examplee'
2929
30-
bencode.encode(12)
31-
# 'i12e'
30+
>>> bencodepy.encode(12)
31+
b'i12e'
3232
3333
**Decode:**
3434

3535
.. code-block:: python
3636
37-
import bencode
37+
>>> import bencodepy
3838
39-
bencode.decode('d5:title7:Examplee')
40-
# {'title': 'Example'}
39+
>>> bencodepy.decode('d5:title7:Examplee')
40+
{b'title': b'Example'}
4141
42-
bencode.decode('i12e')
43-
# 12
42+
>>> bencodepy.decode('i12e')
43+
12
44+
45+
**Decode to UTF-8:**
46+
47+
.. code-block:: python
48+
49+
>>> import bencodepy
50+
51+
>>> bc = bencodepy.Bencode(
52+
encoding='utf-8'
53+
)
54+
55+
>>> bc.decode('d5:title7:Examplee')
56+
{'title': 'Example'}
57+
58+
:code:`bencode`
59+
************************************************
60+
*(legacy, backwards-compatible package)*
61+
62+
This package will continue to be provided for backwards-compatibility, but upgrading to ``bencodepy`` is recommended for more reliable decoding results.
63+
64+
Under-the-hood this just provides proxies to a ``Bencode`` instance created with:
65+
66+
.. code-block:: python
67+
68+
Bencode(
69+
encoding='utf-8',
70+
encoding_fallback='value',
71+
dict_ordered=True,
72+
dict_ordered_sort=True
73+
)
74+
75+
**Encode:**
76+
77+
.. code-block:: python
78+
79+
>>> import bencode
80+
81+
>>> bencode.encode({'title': 'Example'})
82+
'd5:title7:Examplee'
83+
84+
>>> bencode.encode(12)
85+
'i12e'
86+
87+
**Decode:**
88+
89+
.. code-block:: python
90+
91+
>>> import bencode
92+
93+
>>> bencode.decode('d5:title7:Examplee')
94+
OrderedDict([(u'title', u'Example')])
95+
96+
>>> bencode.decode('i12e')
97+
12
4498
4599
46100
API
47101
---
48102

103+
``bencodepy.Bencode(encoding=None, encoding_fallback=None, dict_ordered=False, dict_ordered_sort=False)``
104+
105+
Create instance
106+
107+
- encoding
108+
Encoding to decode strings with (or ``None`` for binary)
109+
- encoding_fallback
110+
Fallback to binary when decoding fails on the specified string types.
111+
112+
- ``key`` - dictionary keys
113+
- ``value`` - values
114+
- ``all`` - always fallback to binary
115+
- ``None`` - always raise decoding errors
116+
- dict_ordered
117+
Use ``OrderedDict``
118+
- dict_ordered_sort
119+
Ensure ``OrderedDict`` is sorted
120+
121+
Methods:
122+
123+
- ``decode(value)``
124+
Decode bencode string ``value``.
125+
126+
- ``encode(value)``
127+
Encode ``value`` into a bencode string.
128+
129+
- ``read(fd)``
130+
Decode bencode from file or path ``fd``.
131+
132+
- ``write(data, fd)``
133+
Encode ``data`` to file or path ``fd``.
134+
135+
``bencodepy.BencodeDecoder(encoding=None, encoding_fallback=None, dict_ordered=False, dict_ordered_sort=False)``
136+
137+
Create decoder
138+
139+
- encoding
140+
Encoding to decode strings with (or ``None`` for binary)
141+
- encoding_fallback
142+
Fallback to binary when decoding fails on the specified string types.
143+
144+
- ``key`` - dictionary keys
145+
- ``value`` - values
146+
- ``all`` - always fallback to binary
147+
- ``None`` - always raise decoding errors
148+
- dict_ordered
149+
Use ``OrderedDict``
150+
- dict_ordered_sort
151+
Ensure ``OrderedDict`` is sorted
152+
153+
Methods:
154+
155+
- ``decode(value)``
156+
Decode bencode string ``value``.
157+
158+
``bencodepy.BencodeEncoder()``
159+
160+
Create encoder
161+
162+
Methods:
163+
164+
- ``encode(value)``
165+
Encode ``value`` into a bencode string.
166+
167+
``bencodepy.bencode(value)``
168+
169+
``bencodepy.encode(value)``
170+
171+
Encode ``value`` into a bencode string with the default encoder.
172+
173+
``bencodepy.bdecode(value)``
174+
175+
``bencodepy.decode(value)``
176+
177+
Decode bencode string ``value`` with the default decoder.
178+
179+
``bencodepy.bread(fd)``
180+
181+
Decode bencode from file or path ``fd`` with the default decoder.
182+
183+
``bencodepy.bwrite(data, fd)``
184+
185+
Encode ``data`` to file or path ``fd`` with the default encoder.
186+
187+
:code:`bencode`
188+
************************************************
189+
49190
``bencode.bencode(value)``
50191

51192
``bencode.encode(value)``

bencode/BTL.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""bencode.py - `BTL` backwards compatibility support."""
2+
3+
# Compatibility with previous versions:
4+
from bencode.exceptions import BencodeDecodeError as BTFailure # noqa: F401
5+
6+
7+
__all__ = (
8+
'BTFailure'
9+
)

bencode/__init__.py

+14-92
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,14 @@
1212

1313
"""bencode.py - bencode encoder + decoder."""
1414

15-
from bencode.common import Bencached
16-
from bencode.decoder import BencodeDecoder
17-
from bencode.encoder import BencodeEncoder
15+
from bencode.BTL import BTFailure
1816
from bencode.exceptions import BencodeDecodeError
19-
20-
try:
21-
import pathlib
22-
except ImportError:
23-
pathlib = None
17+
from bencodepy import Bencached, Bencode
2418

2519
__all__ = (
20+
'BTFailure',
2621
'Bencached',
27-
'Bencode',
28-
'BencodeDecoder',
2922
'BencodeDecodeError',
30-
'BencodeEncoder',
3123
'bencode',
3224
'bdecode',
3325
'bread',
@@ -37,87 +29,17 @@
3729
)
3830

3931

40-
class Bencode(object):
41-
def __init__(self):
42-
self.decoder = BencodeDecoder()
43-
self.encoder = BencodeEncoder()
44-
45-
def decode(self, value):
46-
return self.decoder.decode(value)
47-
48-
def encode(self, value):
49-
return self.encoder.encode(value)
50-
51-
52-
DEFAULT = Bencode()
53-
54-
55-
def bencode(value):
56-
# type: (Union[Tuple, List, OrderedDict, Dict, bool, int, str, bytes]) -> bytes
57-
"""
58-
Encode ``value`` into the bencode format.
59-
60-
:param value: Value
61-
:type value: object
62-
63-
:return: Bencode formatted string
64-
:rtype: str
65-
"""
66-
return DEFAULT.encode(value)
67-
68-
69-
def bdecode(value):
70-
# type: (bytes) -> Union[Tuple, List, OrderedDict, bool, int, str, bytes]
71-
"""
72-
Decode bencode formatted byte string ``value``.
73-
74-
:param value: Bencode formatted string
75-
:type value: bytes
76-
77-
:return: Decoded value
78-
:rtype: object
79-
"""
80-
return DEFAULT.decode(value)
81-
82-
83-
def bread(fd):
84-
# type: (Union[bytes, str, pathlib.Path, pathlib.PurePath, TextIO, BinaryIO]) -> bytes
85-
"""Return bdecoded data from filename, file, or file-like object.
86-
87-
if fd is a bytes/string or pathlib.Path-like object, it is opened and
88-
read, otherwise .read() is used. if read() not available, exception
89-
raised.
90-
"""
91-
if isinstance(fd, (bytes, str)):
92-
with open(fd, 'rb') as fd:
93-
return bdecode(fd.read())
94-
elif pathlib is not None and isinstance(fd, (pathlib.Path, pathlib.PurePath)):
95-
with open(str(fd), 'rb') as fd:
96-
return bdecode(fd.read())
97-
else:
98-
return bdecode(fd.read())
99-
100-
101-
def bwrite(data, # type: Union[Tuple, List, OrderedDict, Dict, bool, int, str, bytes]
102-
fd # type: Union[bytes, str, pathlib.Path, pathlib.PurePath, TextIO, BinaryIO]
103-
):
104-
# type: (...) -> None
105-
"""Write data in bencoded form to filename, file, or file-like object.
106-
107-
if fd is bytes/string or pathlib.Path-like object, it is opened and
108-
written to, otherwise .write() is used. if write() is not available,
109-
exception raised.
110-
"""
111-
if isinstance(fd, (bytes, str)):
112-
with open(fd, 'wb') as fd:
113-
fd.write(bencode(data))
114-
elif pathlib is not None and isinstance(fd, (pathlib.Path, pathlib.PurePath)):
115-
with open(str(fd), 'wb') as fd:
116-
fd.write(bencode(data))
117-
else:
118-
fd.write(bencode(data))
32+
DEFAULT = Bencode(
33+
encoding='utf-8',
34+
encoding_fallback='value',
35+
dict_ordered=True,
36+
dict_ordered_sort=True
37+
)
11938

39+
bdecode = DEFAULT.decode
40+
bencode = DEFAULT.encode
41+
bread = DEFAULT.read
42+
bwrite = DEFAULT.write
12043

121-
# Compatibility Proxies
122-
encode = bencode
12344
decode = bdecode
45+
encode = bencode

bencode/exceptions.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""bencode.py - encoder + decode exceptions."""
2+
from bencodepy.exceptions import BencodeDecodeError
23

3-
4-
class BencodeDecodeError(Exception):
5-
"""Bencode decode error."""
6-
7-
pass
4+
__all__ = (
5+
'BencodeDecodeError',
6+
)

0 commit comments

Comments
 (0)