Skip to content

Commit f946ac4

Browse files
committed
Merge branch 'develop'
2 parents 8a18ce6 + 06f0a89 commit f946ac4

File tree

9 files changed

+96
-62
lines changed

9 files changed

+96
-62
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,5 @@ target/
6060

6161
#Ipython Notebook
6262
.ipynb_checkpoints
63+
64+
bencoder.c

.travis.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ python:
88
- 3.5
99
- pypy
1010

11-
before_install: pip install Cython
12-
install: python setup.py install
11+
install:
12+
- pip install cython
13+
- cython bencoder.pyx
14+
- python setup.py install
1315
script: nosetests

README.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,12 @@ Usage
3232
with open("debian-8.3.0-amd64-netinst.iso.torrent", "rb") as f:
3333
torrent = bdecode(f.read())
3434
print(torrent['announce'])
35+
36+
ChangeLog
37+
----------
38+
39+
Version 1.1.0
40+
~~~~~~~~~~~~~~~
41+
42+
+ Use OrderedDict instaed of dict
43+
+ Support encoding subclasses of dict

appveyor.yml

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -56,53 +56,27 @@ environment:
5656
PYTHON_VERSION: "3.5.0"
5757
PYTHON_ARCH: "64"
5858

59-
install:
60-
- ECHO "Filesystem root:"
61-
- ps: "ls \"C:/\""
62-
63-
- ECHO "Installed SDKs:"
64-
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
59+
init:
60+
- "ECHO Python %PYTHON_VERSION% (%PYTHON_ARCH%bit) from %PYTHON%"
6561

62+
install:
6663
# Install Python (from the official .msi of http://python.org) and pip when
6764
# not already installed.
6865
- ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }
69-
70-
# Prepend newly installed Python to the PATH of this build (this cannot be
71-
# done from inside the powershell script as it would require to restart
72-
# the parent CMD process).
73-
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
74-
75-
# Check that we have the expected version and architecture for Python
76-
- "python --version"
77-
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
78-
79-
# Upgrade to the latest version of pip to avoid it displaying warnings
80-
# about it being out of date.
81-
- "pip install --disable-pip-version-check --user --upgrade pip wheel"
82-
83-
# Install the build dependencies of the project. If some dependencies contain
84-
# compiled extensions and are not provided as pre-built wheel packages,
85-
# pip will build them from source using the MSVC compiler matching the
86-
# target Python version and architecture
87-
- "%CMD_IN_ENV% pip install --user cython"
66+
- "%PYTHON%\\Scripts\\pip install --disable-pip-version-check --user --upgrade pip wheel cython"
8867

8968
build_script:
90-
# Build the compiled extension
91-
- "%CMD_IN_ENV% python setup.py build"
69+
- "%PYTHON%\\Scripts\\cython bencoder.pyx"
70+
- "%PYTHON%\\python setup.py install"
9271

9372
test_script:
94-
# Run the project tests
95-
- "%CMD_IN_ENV% python setup.py test"
73+
- "%PYTHON%\\python setup.py test"
9674

9775
after_test:
98-
# If tests are successful, create binary packages for the project.
99-
- "%CMD_IN_ENV% python setup.py bdist_wheel"
100-
- "%CMD_IN_ENV% python setup.py bdist_wininst"
101-
- "%CMD_IN_ENV% python setup.py bdist_msi"
76+
- "%PYTHON%\\python setup.py bdist_wheel bdist_wininst bdist_msi"
10277
- ps: "ls dist"
10378

10479
artifacts:
105-
# Archive the generated packages in the ci.appveyor.com build report.
10680
- path: dist\*
10781

10882
#on_success:

bencoder.pyx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ __version__ = '1.0.0'
1717
import sys
1818
IS_PY2 = sys.version[0] == '2'
1919

20+
try:
21+
from collections import OrderedDict
22+
except ImportError:
23+
from ordereddict import OrderedDict
24+
2025
if IS_PY2:
2126
END_CHAR = 'e'
2227
else:
@@ -57,7 +62,7 @@ def decode_list(bytes x, int f):
5762

5863

5964
def decode_dict(bytes x, int f):
60-
r, f = {}, f + 1
65+
r, f = OrderedDict(), f + 1
6166
while x[f] != END_CHAR:
6267
k, f = decode_string(x, f)
6368
r[k], f = decode_func[x[f]](x, f)
@@ -89,6 +94,18 @@ def bdecode(bytes x):
8994
return r
9095

9196

97+
def encode(v, r):
98+
tp = type(v)
99+
if tp in encode_func:
100+
return encode_func[tp](v, r)
101+
else:
102+
for tp, func in encode_func.items():
103+
if isinstance(v, tp):
104+
return func(v, r)
105+
raise BTFailure("Unknown Type: %s" % tp)
106+
107+
108+
92109
def encode_int(int x, list r):
93110
r.extend((b'i', str(x).encode(), b'e'))
94111

@@ -109,19 +126,19 @@ def encode_string(x, list r):
109126
def encode_list(x, list r):
110127
r.append(b'l')
111128
for i in x:
112-
encode_func[type(i)](i, r)
129+
encode(i, r)
113130
r.append(b'e')
114131

115132

116-
def encode_dict(dict x, list r):
133+
def encode_dict(x, list r):
117134
r.append(b'd')
118135
item_list = list(x.items())
119136
item_list.sort()
120137
for k, v in item_list:
121138
if isinstance(k, str):
122139
k = k.encode()
123140
r.extend((str(len(k)).encode(), b':', k))
124-
encode_func[type(v)](v, r)
141+
encode(v, r)
125142
r.append(b'e')
126143

127144

@@ -132,11 +149,12 @@ encode_func = {
132149
list: encode_list,
133150
tuple: encode_list,
134151
dict: encode_dict,
152+
OrderedDict: encode_dict,
135153
bool: encode_bool,
136154
}
137155

138156

139157
def bencode(x):
140158
r = []
141-
encode_func[type(x)](x, r)
159+
encode(x, r)
142160
return b''.join(r)

release.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
cython bencoder.pyx
12
python setup.py sdist --formats=zip,gztar bdist_wheel register
23
twine upload dist/*

setup.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
import sys
1+
import platform
22

33
from setuptools import setup
44
from setuptools.extension import Extension
5-
from setuptools.dist import Distribution
6-
Distribution(dict(setup_requires='Cython'))
75

8-
try:
9-
from Cython.Distutils import build_ext
10-
except ImportError:
11-
print('Could not import Cython.Distutils. Install `cython` and rerun.')
12-
sys.exit(1)
13-
14-
ext_modules = [Extension('bencoder', ['bencoder.pyx'])]
6+
version = platform.python_version_tuple()
7+
install_requires = []
8+
if version < ('2', '7'):
9+
install_requires.append('ordereddict>=1.1')
1510

1611
setup(
1712
name='bencoder.pyx',
18-
version='1.0.0',
13+
version='1.1.0',
1914
description='Yet another bencode implementation in Cython',
2015
long_description=open('README.rst', 'r').read(),
2116
author='whtsky',
@@ -44,9 +39,8 @@
4439
'Programming Language :: Python :: Implementation :: PyPy',
4540
'Topic :: Software Development :: Libraries :: Python Modules',
4641
],
47-
cmdclass={'build_ext': build_ext},
48-
ext_modules=ext_modules,
49-
setup_requires=['Cython'],
42+
ext_modules=[Extension('bencoder', ['bencoder.c'])],
43+
install_requires=install_requires,
5044
tests_require=['nose'],
5145
test_suite='nose.collector',
5246
)

tests/test_decode.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# -*- coding: utf-8 -*-
22

33
from bencoder import bdecode
4+
import os
5+
6+
TORRENT_PATH = os.path.join(
7+
os.path.abspath(os.path.dirname(__file__)),
8+
"debian-8.3.0-amd64-netinst.iso.torrent"
9+
)
410

511

612
def test_decode_str():
@@ -22,6 +28,15 @@ def test_decode_dict():
2228
assert bdecode(b'd2:ka2:va2:kbi2ee') == od
2329

2430

31+
def test_ordered_dict():
32+
from bencoder import OrderedDict
33+
rv = bdecode(b'd2:ka2:va2:kbi2ee')
34+
assert isinstance(rv, OrderedDict)
35+
assert list(rv.keys()) == [b'ka', b'kb']
36+
assert list(bdecode(b'd2:kc2:va2:kei2ee').keys()) == [b'kc', b'ke']
37+
assert list(bdecode(b'd2:ke2:va2:kci2ee').keys()) == [b'ke', b'kc']
38+
39+
2540
def test_encode_complex():
2641
od = dict()
2742
od[b'KeyA'] = [b'listitemA', {b'k': b'v'}, 3]
@@ -33,12 +48,7 @@ def test_encode_complex():
3348

3449

3550
def test_decode_debian_torrent():
36-
import os
37-
torrent_path = os.path.join(
38-
os.path.abspath(os.path.dirname(__file__)),
39-
"debian-8.3.0-amd64-netinst.iso.torrent"
40-
)
41-
with open(torrent_path, "rb") as f:
51+
with open(TORRENT_PATH, "rb") as f:
4252
torrent = bdecode(f.read())
4353
assert torrent[b'announce'] == b'http://bttracker.debian.org:6969/announce'
4454
assert torrent[b'comment'] == b'"Debian CD from cdimage.debian.org"'

tests/test_encode.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# -*- coding: utf-8 -*-
22

3-
from bencoder import bencode
3+
from bencoder import bencode, bdecode
4+
import os
5+
6+
TORRENT_PATH = os.path.join(
7+
os.path.abspath(os.path.dirname(__file__)),
8+
"debian-8.3.0-amd64-netinst.iso.torrent"
9+
)
410

511

612
def test_encode_str():
@@ -33,6 +39,16 @@ def test_encode_dict():
3339
assert bencode(od) == b'd2:ka2:va2:kbi2ee'
3440

3541

42+
def test_encode_dict_subclass():
43+
class AAA(dict):
44+
pass
45+
46+
od = dict()
47+
od['ka'] = 'va'
48+
od['kb'] = 2
49+
assert bencode(od) == b'd2:ka2:va2:kbi2ee'
50+
51+
3652
def test_encode_complex():
3753
od = dict()
3854
od['KeyA'] = ['listitemA', {'k': 'v'}, 3]
@@ -41,3 +57,11 @@ def test_encode_complex():
4157
od['KeyD'] = 'AString'
4258
expected_result = b'd4:KeyAl9:listitemAd1:k1:vei3ee4:KeyBd1:k1:ve4:KeyCi3e4:KeyD7:AStringe'
4359
assert bencode(od) == expected_result
60+
61+
62+
def test_infohash():
63+
import hashlib
64+
with open(TORRENT_PATH, "rb") as f:
65+
torrent = bdecode(f.read())
66+
infohash = hashlib.sha1(bencode(torrent[b'info'])).hexdigest()
67+
assert infohash == "4194e473d6c49630e1c6247d6716076809bb96ae"

0 commit comments

Comments
 (0)