fastbencode 0.2__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {fastbencode-0.2 → fastbencode-0.3.1}/PKG-INFO +16 -7
  2. {fastbencode-0.2 → fastbencode-0.3.1}/README.md +6 -0
  3. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode/__init__.py +6 -7
  4. fastbencode-0.3.1/fastbencode/_bencode_py.py +186 -0
  5. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode/_bencode_pyx.pyi +1 -1
  6. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode/_bencode_pyx.pyx +62 -12
  7. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode/tests/test_bencode.py +86 -13
  8. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode.egg-info/PKG-INFO +16 -7
  9. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode.egg-info/SOURCES.txt +0 -8
  10. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode.egg-info/requires.txt +3 -0
  11. fastbencode-0.3.1/pyproject.toml +75 -0
  12. fastbencode-0.3.1/setup.cfg +4 -0
  13. {fastbencode-0.2 → fastbencode-0.3.1}/setup.py +6 -4
  14. fastbencode-0.2/.github/workflows/disperse.yml +0 -24
  15. fastbencode-0.2/.github/workflows/pythonpackage.yml +0 -32
  16. fastbencode-0.2/.github/workflows/pythonpublish.yml +0 -58
  17. fastbencode-0.2/.gitignore +0 -8
  18. fastbencode-0.2/CODE_OF_CONDUCT.md +0 -76
  19. fastbencode-0.2/SECURITY.md +0 -5
  20. fastbencode-0.2/disperse.conf +0 -8
  21. fastbencode-0.2/fastbencode/_bencode_py.py +0 -162
  22. fastbencode-0.2/pyproject.toml +0 -3
  23. fastbencode-0.2/setup.cfg +0 -35
  24. {fastbencode-0.2 → fastbencode-0.3.1}/COPYING +0 -0
  25. {fastbencode-0.2 → fastbencode-0.3.1}/MANIFEST.in +0 -0
  26. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode/_bencode_pyx.h +0 -0
  27. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode/py.typed +0 -0
  28. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode/python-compat.h +0 -0
  29. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode/tests/__init__.py +0 -0
  30. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode.egg-info/dependency_links.txt +0 -0
  31. {fastbencode-0.2 → fastbencode-0.3.1}/fastbencode.egg-info/top_level.txt +0 -0
@@ -1,24 +1,27 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastbencode
3
- Version: 0.2
3
+ Version: 0.3.1
4
4
  Summary: Implementation of bencode with optional fast C extensions
5
- Home-page: https://github.com/breezy-team/fastbencode
6
- Maintainer: Breezy Developers
7
- Maintainer-email: breezy-core@googlegroups.com
5
+ Maintainer-email: Breezy Developers <breezy-core@googlegroups.com>
8
6
  License: GPLv2 or later
7
+ Project-URL: Homepage, https://github.com/breezy-team/fastbencode
9
8
  Project-URL: GitHub, https://github.com/breezy-team/fastbencode
10
- Classifier: Programming Language :: Python :: 3.7
11
9
  Classifier: Programming Language :: Python :: 3.8
12
10
  Classifier: Programming Language :: Python :: 3.9
13
11
  Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: Implementation :: CPython
16
15
  Classifier: Programming Language :: Python :: Implementation :: PyPy
17
16
  Classifier: Operating System :: POSIX
18
17
  Classifier: Operating System :: Microsoft :: Windows
19
- Requires-Python: >=3.7
20
- Provides-Extra: cext
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
21
20
  License-File: COPYING
21
+ Provides-Extra: cext
22
+ Requires-Dist: cython>=0.29; extra == "cext"
23
+ Provides-Extra: dev
24
+ Requires-Dist: ruff==0.4.3; extra == "dev"
22
25
 
23
26
  fastbencode
24
27
  ===========
@@ -38,6 +41,12 @@ Example:
38
41
  >>> bdecode(bencode([1, 2, b'a', {b'd': 3}]))
39
42
  [1, 2, b'a', {b'd': 3}]
40
43
 
44
+ The default ``bencode``/``bdecode`` functions just operate on
45
+ bytestrings. Use ``bencode_utf8`` / ``bdecode_utf8`` to
46
+ serialize/deserialize all plain strings as UTF-8 bytestrings.
47
+ Note that for performance reasons, all dictionary keys still have to be
48
+ bytestrings.
49
+
41
50
  License
42
51
  =======
43
52
  fastbencode is available under the GNU GPL, version 2 or later.
@@ -16,6 +16,12 @@ Example:
16
16
  >>> bdecode(bencode([1, 2, b'a', {b'd': 3}]))
17
17
  [1, 2, b'a', {b'd': 3}]
18
18
 
19
+ The default ``bencode``/``bdecode`` functions just operate on
20
+ bytestrings. Use ``bencode_utf8`` / ``bdecode_utf8`` to
21
+ serialize/deserialize all plain strings as UTF-8 bytestrings.
22
+ Note that for performance reasons, all dictionary keys still have to be
23
+ bytestrings.
24
+
19
25
  License
20
26
  =======
21
27
  fastbencode is available under the GNU GPL, version 2 or later.
@@ -14,12 +14,11 @@
14
14
  # along with this program; if not, write to the Free Software
15
15
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
- """Wrapper around the bencode cython and python implementation"""
17
+ """Wrapper around the bencode cython and python implementation."""
18
18
 
19
19
  from typing import Type
20
20
 
21
-
22
- __version__ = (0, 2)
21
+ __version__ = (0, 3, 1)
23
22
 
24
23
 
25
24
  _extension_load_failures = []
@@ -50,7 +49,7 @@ def failed_to_load_extension(exception):
50
49
  if exception_str not in _extension_load_failures:
51
50
  import warnings
52
51
  warnings.warn(
53
- 'failed to load compiled extension: %s' % exception_str,
52
+ f'failed to load compiled extension: {exception_str}',
54
53
  UserWarning)
55
54
  _extension_load_failures.append(exception_str)
56
55
 
@@ -58,12 +57,12 @@ def failed_to_load_extension(exception):
58
57
  Bencached: Type
59
58
 
60
59
  try:
61
- from ._bencode_pyx import bdecode, bdecode_as_tuple, bencode, Bencached
60
+ from ._bencode_pyx import Bencached, bdecode, bdecode_as_tuple, bencode
62
61
  except ImportError as e:
63
62
  failed_to_load_extension(e)
64
63
  from ._bencode_py import ( # noqa: F401
64
+ Bencached,
65
65
  bdecode,
66
66
  bdecode_as_tuple,
67
67
  bencode,
68
- Bencached,
69
- )
68
+ )
@@ -0,0 +1,186 @@
1
+ # bencode structured encoding
2
+ #
3
+ # Written by Petru Paler
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person
6
+ # obtaining a copy of this software and associated documentation files
7
+ # (the "Software"), to deal in the Software without restriction,
8
+ # including without limitation the rights to use, copy, modify, merge,
9
+ # publish, distribute, sublicense, and/or sell copies of the Software,
10
+ # and to permit persons to whom the Software is furnished to do so,
11
+ # subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # Modifications copyright (C) 2008 Canonical Ltd
17
+
18
+
19
+ from typing import Callable, Dict, List, Type
20
+
21
+
22
+ class BDecoder:
23
+
24
+ def __init__(self, yield_tuples=False, bytestring_encoding=None) -> None:
25
+ """Constructor.
26
+
27
+ :param yield_tuples: if true, decode "l" elements as tuples rather than
28
+ lists.
29
+ """
30
+ self.yield_tuples = yield_tuples
31
+ self.bytestring_encoding = bytestring_encoding
32
+ decode_func = {}
33
+ decode_func[b'l'] = self.decode_list
34
+ decode_func[b'd'] = self.decode_dict
35
+ decode_func[b'i'] = self.decode_int
36
+ decode_func[b'0'] = self.decode_bytes
37
+ decode_func[b'1'] = self.decode_bytes
38
+ decode_func[b'2'] = self.decode_bytes
39
+ decode_func[b'3'] = self.decode_bytes
40
+ decode_func[b'4'] = self.decode_bytes
41
+ decode_func[b'5'] = self.decode_bytes
42
+ decode_func[b'6'] = self.decode_bytes
43
+ decode_func[b'7'] = self.decode_bytes
44
+ decode_func[b'8'] = self.decode_bytes
45
+ decode_func[b'9'] = self.decode_bytes
46
+ self.decode_func = decode_func
47
+
48
+ def decode_int(self, x, f):
49
+ f += 1
50
+ newf = x.index(b'e', f)
51
+ n = int(x[f:newf])
52
+ if x[f:f + 2] == b'-0':
53
+ raise ValueError
54
+ elif x[f:f + 1] == b'0' and newf != f + 1:
55
+ raise ValueError
56
+ return (n, newf + 1)
57
+
58
+ def decode_bytes(self, x, f):
59
+ colon = x.index(b':', f)
60
+ n = int(x[f:colon])
61
+ if x[f:f + 1] == b'0' and colon != f + 1:
62
+ raise ValueError
63
+ colon += 1
64
+ d = x[colon:colon + n]
65
+ if self.bytestring_encoding:
66
+ d = d.decode(self.bytestring_encoding)
67
+ return (d, colon + n)
68
+
69
+ def decode_list(self, x, f):
70
+ r, f = [], f + 1
71
+ while x[f:f + 1] != b'e':
72
+ v, f = self.decode_func[x[f:f + 1]](x, f)
73
+ r.append(v)
74
+ if self.yield_tuples:
75
+ r = tuple(r)
76
+ return (r, f + 1)
77
+
78
+ def decode_dict(self, x, f):
79
+ r, f = {}, f + 1
80
+ lastkey = None
81
+ while x[f:f + 1] != b'e':
82
+ k, f = self.decode_bytes(x, f)
83
+ if lastkey is not None and lastkey >= k:
84
+ raise ValueError
85
+ lastkey = k
86
+ r[k], f = self.decode_func[x[f:f + 1]](x, f)
87
+ return (r, f + 1)
88
+
89
+ def bdecode(self, x):
90
+ if not isinstance(x, bytes):
91
+ raise TypeError
92
+ try:
93
+ r, l = self.decode_func[x[:1]](x, 0) # noqa: E741
94
+ except (IndexError, KeyError, OverflowError) as e:
95
+ raise ValueError(str(e))
96
+ if l != len(x): # noqa: E741
97
+ raise ValueError
98
+ return r
99
+
100
+
101
+ _decoder = BDecoder()
102
+ bdecode = _decoder.bdecode
103
+
104
+ _tuple_decoder = BDecoder(True)
105
+ bdecode_as_tuple = _tuple_decoder.bdecode
106
+
107
+ _utf8_decoder = BDecoder(bytestring_encoding='utf-8')
108
+ bdecode_utf8 = _utf8_decoder.bdecode
109
+
110
+
111
+ class Bencached:
112
+ __slots__ = ['bencoded']
113
+
114
+ def __init__(self, s) -> None:
115
+ self.bencoded = s
116
+
117
+
118
+ class BEncoder:
119
+
120
+ def __init__(self, bytestring_encoding=None):
121
+ self.bytestring_encoding = bytestring_encoding
122
+ self.encode_func: Dict[Type, Callable[[object, List[bytes]], None]] = {
123
+ Bencached: self.encode_bencached,
124
+ int: self.encode_int,
125
+ bytes: self.encode_bytes,
126
+ list: self.encode_list,
127
+ tuple: self.encode_list,
128
+ dict: self.encode_dict,
129
+ bool: self.encode_bool,
130
+ str: self.encode_str,
131
+ }
132
+
133
+ def encode_bencached(self, x, r):
134
+ r.append(x.bencoded)
135
+
136
+
137
+ def encode_bool(self, x, r):
138
+ self.encode_int(int(x), r)
139
+
140
+
141
+ def encode_int(self, x, r):
142
+ r.extend((b'i', int_to_bytes(x), b'e'))
143
+
144
+
145
+ def encode_bytes(self, x, r):
146
+ r.extend((int_to_bytes(len(x)), b':', x))
147
+
148
+ def encode_list(self, x, r):
149
+ r.append(b'l')
150
+ for i in x:
151
+ self.encode(i, r)
152
+ r.append(b'e')
153
+
154
+
155
+ def encode_dict(self, x, r):
156
+ r.append(b'd')
157
+ ilist = sorted(x.items())
158
+ for k, v in ilist:
159
+ r.extend((int_to_bytes(len(k)), b':', k))
160
+ self.encode(v, r)
161
+ r.append(b'e')
162
+
163
+ def encode_str(self, x, r):
164
+ if self.bytestring_encoding is None:
165
+ raise TypeError("string found but no encoding specified. "
166
+ "Use bencode_utf8 rather bencode?")
167
+ return self.encode_bytes(x.encode(self.bytestring_encoding), r)
168
+
169
+ def encode(self, x, r):
170
+ self.encode_func[type(x)](x, r)
171
+
172
+
173
+ def int_to_bytes(n):
174
+ return b'%d' % n
175
+
176
+ def bencode(x):
177
+ r = []
178
+ encoder = BEncoder()
179
+ encoder.encode(x, r)
180
+ return b''.join(r)
181
+
182
+ def bencode_utf8(x):
183
+ r = []
184
+ encoder = BEncoder(bytestring_encoding='utf-8')
185
+ encoder.encode(x, r)
186
+ return b''.join(r)
@@ -4,4 +4,4 @@ def bdecode_as_tuple(bytes) -> object: ...
4
4
  def bencode(object) -> bytes: ...
5
5
 
6
6
 
7
- class Bencached(object): ...
7
+ class Bencached: ...
@@ -46,15 +46,22 @@ from cpython.mem cimport (
46
46
  PyMem_Malloc,
47
47
  PyMem_Realloc,
48
48
  )
49
+ from cpython.unicode cimport (
50
+ PyUnicode_FromEncodedObject,
51
+ PyUnicode_FromStringAndSize,
52
+ PyUnicode_Check,
53
+ )
49
54
  from cpython.tuple cimport (
50
55
  PyTuple_CheckExact,
51
56
  )
52
57
 
53
58
  from libc.stdlib cimport (
54
59
  strtol,
60
+ free,
55
61
  )
56
62
  from libc.string cimport (
57
63
  memcpy,
64
+ strdup,
58
65
  )
59
66
 
60
67
  cdef extern from "python-compat.h":
@@ -79,9 +86,10 @@ cdef class Decoder:
79
86
  cdef readonly char *tail
80
87
  cdef readonly int size
81
88
  cdef readonly int _yield_tuples
89
+ cdef readonly char *_bytestring_encoding
82
90
  cdef object text
83
91
 
84
- def __init__(self, s, yield_tuples=0):
92
+ def __init__(self, s, yield_tuples=0, str bytestring_encoding=None):
85
93
  """Initialize decoder engine.
86
94
  @param s: Python string.
87
95
  """
@@ -92,6 +100,13 @@ cdef class Decoder:
92
100
  self.tail = PyBytes_AS_STRING(s)
93
101
  self.size = PyBytes_GET_SIZE(s)
94
102
  self._yield_tuples = int(yield_tuples)
103
+ if bytestring_encoding is None:
104
+ self._bytestring_encoding = NULL
105
+ else:
106
+ self._bytestring_encoding = strdup(bytestring_encoding.encode('utf-8'))
107
+
108
+ def __dealloc__(self):
109
+ free(self._bytestring_encoding)
95
110
 
96
111
  def decode(self):
97
112
  result = self._decode_object()
@@ -112,7 +127,7 @@ cdef class Decoder:
112
127
  try:
113
128
  ch = self.tail[0]
114
129
  if c'0' <= ch <= c'9':
115
- return self._decode_string()
130
+ return self._decode_bytes()
116
131
  elif ch == c'l':
117
132
  D_UPDATE_TAIL(self, 1)
118
133
  return self._decode_list()
@@ -155,12 +170,12 @@ cdef class Decoder:
155
170
  D_UPDATE_TAIL(self, i+1)
156
171
  return ret
157
172
 
158
- cdef object _decode_string(self):
173
+ cdef object _decode_bytes(self):
159
174
  cdef int n
160
175
  cdef char *next_tail
161
176
  # strtol allows leading whitespace, negatives, and leading zeros
162
177
  # however, all callers have already checked that '0' <= tail[0] <= '9'
163
- # or they wouldn't have called _decode_string
178
+ # or they wouldn't have called _decode_bytes
164
179
  # strtol will stop at trailing whitespace, etc
165
180
  n = strtol(self.tail, &next_tail, 10)
166
181
  if next_tail == NULL or next_tail[0] != c':':
@@ -171,13 +186,22 @@ cdef class Decoder:
171
186
  raise ValueError('leading zeros are not allowed')
172
187
  D_UPDATE_TAIL(self, next_tail - self.tail + 1)
173
188
  if n == 0:
174
- return b''
189
+ if self._bytestring_encoding == NULL:
190
+ return b''
191
+ else:
192
+ return ''
175
193
  if n > self.size:
176
194
  raise ValueError('stream underflow')
177
195
  if n < 0:
178
196
  raise ValueError('string size below zero: %d' % n)
179
197
 
180
- result = PyBytes_FromStringAndSize(self.tail, n)
198
+ if self._bytestring_encoding == NULL:
199
+ result = PyBytes_FromStringAndSize(self.tail, n)
200
+ elif self._bytestring_encoding == b'utf-8':
201
+ result = PyUnicode_FromStringAndSize(self.tail, n)
202
+ else:
203
+ result = PyBytes_FromStringAndSize(self.tail, n)
204
+ result = PyUnicode_FromEncodedObject(result, self._bytestring_encoding, NULL)
181
205
  D_UPDATE_TAIL(self, n)
182
206
  return result
183
207
 
@@ -214,7 +238,7 @@ cdef class Decoder:
214
238
  # keys should be strings only
215
239
  if self.tail[0] < c'0' or self.tail[0] > c'9':
216
240
  raise ValueError('key was not a simple string.')
217
- key = self._decode_string()
241
+ key = self._decode_bytes()
218
242
  if lastkey is not None and lastkey >= key:
219
243
  raise ValueError('dict keys disordered')
220
244
  else:
@@ -235,6 +259,11 @@ def bdecode_as_tuple(object s):
235
259
  return Decoder(s, True).decode()
236
260
 
237
261
 
262
+ def bdecode_utf8(object s):
263
+ """Decode string x to Python object, decoding bytestrings as UTF8 strings."""
264
+ return Decoder(s, bytestring_encoding='utf-8').decode()
265
+
266
+
238
267
  class Bencached(object):
239
268
  __slots__ = ['bencoded']
240
269
 
@@ -254,8 +283,9 @@ cdef class Encoder:
254
283
  cdef readonly int size
255
284
  cdef readonly char *buffer
256
285
  cdef readonly int maxsize
286
+ cdef readonly object _bytestring_encoding
257
287
 
258
- def __init__(self, int maxsize=INITSIZE):
288
+ def __init__(self, int maxsize=INITSIZE, str bytestring_encoding=None):
259
289
  """Initialize encoder engine
260
290
  @param maxsize: initial size of internal char buffer
261
291
  """
@@ -273,6 +303,8 @@ cdef class Encoder:
273
303
  self.maxsize = maxsize
274
304
  self.tail = p
275
305
 
306
+ self._bytestring_encoding = bytestring_encoding
307
+
276
308
  def __dealloc__(self):
277
309
  PyMem_Free(self.buffer)
278
310
  self.buffer = NULL
@@ -329,7 +361,7 @@ cdef class Encoder:
329
361
  E_UPDATE_TAIL(self, n)
330
362
  return 1
331
363
 
332
- cdef int _encode_string(self, x) except 0:
364
+ cdef int _encode_bytes(self, x) except 0:
333
365
  cdef int n
334
366
  cdef Py_ssize_t x_len
335
367
  x_len = PyBytes_GET_SIZE(x)
@@ -341,6 +373,12 @@ cdef class Encoder:
341
373
  E_UPDATE_TAIL(self, n + x_len)
342
374
  return 1
343
375
 
376
+ cdef int _encode_string(self, x) except 0:
377
+ if self._bytestring_encoding is None:
378
+ raise TypeError("string found but no encoding specified. "
379
+ "Use bencode_utf8 rather bencode?")
380
+ return self._encode_bytes(x.encode(self._bytestring_encoding))
381
+
344
382
  cdef int _encode_list(self, x) except 0:
345
383
  self._ensure_buffer(1)
346
384
  self.tail[0] = c'l'
@@ -362,7 +400,7 @@ cdef class Encoder:
362
400
  for k in sorted(x):
363
401
  if not PyBytes_CheckExact(k):
364
402
  raise TypeError('key in dict should be string')
365
- self._encode_string(k)
403
+ self._encode_bytes(k)
366
404
  self.process(x[k])
367
405
 
368
406
  self._ensure_buffer(1)
@@ -374,7 +412,7 @@ cdef class Encoder:
374
412
  BrzPy_EnterRecursiveCall(" while bencode encoding")
375
413
  try:
376
414
  if PyBytes_CheckExact(x):
377
- self._encode_string(x)
415
+ self._encode_bytes(x)
378
416
  elif PyInt_CheckExact(x) and x.bit_length() < 32:
379
417
  self._encode_int(x)
380
418
  elif PyLong_CheckExact(x):
@@ -385,6 +423,8 @@ cdef class Encoder:
385
423
  self._encode_dict(x)
386
424
  elif PyBool_Check(x):
387
425
  self._encode_int(int(x))
426
+ elif PyUnicode_Check(x):
427
+ self._encode_string(x)
388
428
  elif isinstance(x, Bencached):
389
429
  self._append_string(x.bencoded)
390
430
  else:
@@ -394,7 +434,17 @@ cdef class Encoder:
394
434
 
395
435
 
396
436
  def bencode(x):
397
- """Encode Python object x to string"""
437
+ """Encode Python object x to bytestring"""
398
438
  encoder = Encoder()
399
439
  encoder.process(x)
400
440
  return encoder.to_bytes()
441
+
442
+
443
+ def bencode_utf8(x):
444
+ """Encode Python object x to bytestring.
445
+
446
+ Encode any strings as UTF8
447
+ """
448
+ encoder = Encoder(bytestring_encoding='utf-8')
449
+ encoder.process(x)
450
+ return encoder.to_bytes()
@@ -14,11 +14,10 @@
14
14
  # along with this program; if not, write to the Free Software
15
15
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
- """Tests for bencode structured encoding"""
17
+ """Tests for bencode structured encoding."""
18
18
 
19
19
  import copy
20
20
  import sys
21
-
22
21
  from unittest import TestCase, TestSuite
23
22
 
24
23
 
@@ -62,15 +61,14 @@ def get_named_object(module_name, member_name=None):
62
61
 
63
62
 
64
63
  def iter_suite_tests(suite):
65
- """Return all tests in a suite, recursing through nested suites"""
64
+ """Return all tests in a suite, recursing through nested suites."""
66
65
  if isinstance(suite, TestCase):
67
66
  yield suite
68
67
  elif isinstance(suite, TestSuite):
69
68
  for item in suite:
70
69
  yield from iter_suite_tests(item)
71
70
  else:
72
- raise Exception('unknown type %r for object %r'
73
- % (type(suite), suite))
71
+ raise Exception(f'unknown type {type(suite)!r} for object {suite!r}')
74
72
 
75
73
 
76
74
  def clone_test(test, new_id):
@@ -107,7 +105,7 @@ def apply_scenario(test, scenario):
107
105
  test.
108
106
  :return: The adapted test.
109
107
  """
110
- new_id = "{}({})".format(test.id(), scenario[0])
108
+ new_id = f"{test.id()}({scenario[0]})"
111
109
  new_test = clone_test(test, new_id)
112
110
  for name, value in scenario[1].items():
113
111
  setattr(new_test, name, value)
@@ -188,7 +186,6 @@ def permute_tests_for_extension(standard_tests, loader, py_module_name,
188
186
  tests. feature is the Feature object that can be used to determine if
189
187
  the module is available.
190
188
  """
191
-
192
189
  py_module = get_named_object(py_module_name)
193
190
  scenarios = [
194
191
  ('python', {'module': py_module}),
@@ -213,7 +210,7 @@ def load_tests(loader, standard_tests, pattern):
213
210
  class RecursionLimit:
214
211
  """Context manager that lowers recursion limit for testing."""
215
212
 
216
- def __init__(self, limit=100):
213
+ def __init__(self, limit=100) -> None:
217
214
  self._new_limit = limit
218
215
  self._old_limit = sys.getrecursionlimit()
219
216
 
@@ -290,10 +287,16 @@ class TestBencodeDecode(TestCase):
290
287
 
291
288
  def test_list_deepnested(self):
292
289
  import platform
293
- if platform.python_implementation() == 'PyPy':
294
- self.skipTest('recursion not an issue on pypy')
295
- with RecursionLimit():
296
- self._run_check_error(RuntimeError, (b"l" * 100) + (b"e" * 100))
290
+ if (platform.python_implementation() == 'PyPy'
291
+ or sys.version_info[:2] >= (3, 12)):
292
+ expected = []
293
+ for i in range(99):
294
+ expected = [expected]
295
+ self._check(expected, (b"l" * 100) + (b"e" * 100))
296
+ else:
297
+ with RecursionLimit():
298
+ self._run_check_error(
299
+ RuntimeError, (b"l" * 100) + (b"e" * 100))
297
300
 
298
301
  def test_malformed_list(self):
299
302
  self._run_check_error(ValueError, b'l')
@@ -338,7 +341,7 @@ class TestBencodeDecode(TestCase):
338
341
  self.assertRaises(ValueError, self.module.bdecode, b'relwjhrlewjh')
339
342
 
340
343
  def test_unsupported_type(self):
341
- self._run_check_error(TypeError, float(1.5))
344
+ self._run_check_error(TypeError, 1.5)
342
345
  self._run_check_error(TypeError, None)
343
346
  self._run_check_error(TypeError, lambda x: x)
344
347
  self._run_check_error(TypeError, object)
@@ -348,6 +351,47 @@ class TestBencodeDecode(TestCase):
348
351
  self.assertRaises(TypeError, self.module.bdecode, 1)
349
352
 
350
353
 
354
+ class TestBdecodeUtf8(TestCase):
355
+
356
+ module = None
357
+
358
+ def _check(self, expected, source):
359
+ self.assertEqual(expected, self.module.bdecode_utf8(source))
360
+
361
+ def _run_check_error(self, exc, bad):
362
+ """Check that bdecoding a string raises a particular exception."""
363
+ self.assertRaises(exc, self.module.bdecode_utf8, bad)
364
+
365
+ def test_string(self):
366
+ self._check('', b'0:')
367
+ self._check('aäc', b'4:a\xc3\xa4c')
368
+ self._check('1234567890', b'10:1234567890')
369
+
370
+ def test_large_string(self):
371
+ self.assertRaises(
372
+ ValueError, self.module.bdecode_utf8, b"2147483639:foo")
373
+
374
+ def test_malformed_string(self):
375
+ self._run_check_error(ValueError, b'10:x')
376
+ self._run_check_error(ValueError, b'10:')
377
+ self._run_check_error(ValueError, b'10')
378
+ self._run_check_error(ValueError, b'01:x')
379
+ self._run_check_error(ValueError, b'00:')
380
+ self._run_check_error(ValueError, b'35208734823ljdahflajhdf')
381
+ self._run_check_error(ValueError, b'432432432432432:foo')
382
+ self._run_check_error(ValueError, b' 1:x') # leading whitespace
383
+ self._run_check_error(ValueError, b'-1:x') # negative
384
+ self._run_check_error(ValueError, b'1 x') # space vs colon
385
+ self._run_check_error(ValueError, b'1x') # missing colon
386
+ self._run_check_error(ValueError, (b'1' * 1000) + b':')
387
+
388
+ def test_empty_string(self):
389
+ self.assertRaises(ValueError, self.module.bdecode_utf8, b'')
390
+
391
+ def test_invalid_utf8(self):
392
+ self._run_check_error(UnicodeDecodeError, b'3:\xff\xfe\xfd')
393
+
394
+
351
395
  class TestBencodeEncode(TestCase):
352
396
 
353
397
  module = None
@@ -411,3 +455,32 @@ class TestBencodeEncode(TestCase):
411
455
  def test_bool(self):
412
456
  self._check(b'i1e', True)
413
457
  self._check(b'i0e', False)
458
+
459
+
460
+ class TestBencodeEncodeUtf8(TestCase):
461
+
462
+ module = None
463
+
464
+ def _check(self, expected, source):
465
+ self.assertEqual(expected, self.module.bencode_utf8(source))
466
+
467
+ def test_string(self):
468
+ self._check(b'0:', '')
469
+ self._check(b'3:abc', 'abc')
470
+ self._check(b'10:1234567890', '1234567890')
471
+
472
+ def test_list(self):
473
+ self._check(b'le', [])
474
+ self._check(b'li1ei2ei3ee', [1, 2, 3])
475
+ self._check(b'll5:Alice3:Bobeli2ei3eee', [['Alice', 'Bob'], [2, 3]])
476
+
477
+ def test_list_as_tuple(self):
478
+ self._check(b'le', ())
479
+ self._check(b'li1ei2ei3ee', (1, 2, 3))
480
+ self._check(b'll5:Alice3:Bobeli2ei3eee', (('Alice', 'Bob'), (2, 3)))
481
+
482
+ def test_dict(self):
483
+ self._check(b'de', {})
484
+ self._check(b'd3:agei25e4:eyes4:bluee', {b'age': 25, b'eyes': 'blue'})
485
+ self._check(b'd8:spam.mp3d6:author5:Alice6:lengthi100000eee',
486
+ {b'spam.mp3': {b'author': b'Alice', b'length': 100000}})
@@ -1,24 +1,27 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastbencode
3
- Version: 0.2
3
+ Version: 0.3.1
4
4
  Summary: Implementation of bencode with optional fast C extensions
5
- Home-page: https://github.com/breezy-team/fastbencode
6
- Maintainer: Breezy Developers
7
- Maintainer-email: breezy-core@googlegroups.com
5
+ Maintainer-email: Breezy Developers <breezy-core@googlegroups.com>
8
6
  License: GPLv2 or later
7
+ Project-URL: Homepage, https://github.com/breezy-team/fastbencode
9
8
  Project-URL: GitHub, https://github.com/breezy-team/fastbencode
10
- Classifier: Programming Language :: Python :: 3.7
11
9
  Classifier: Programming Language :: Python :: 3.8
12
10
  Classifier: Programming Language :: Python :: 3.9
13
11
  Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: Implementation :: CPython
16
15
  Classifier: Programming Language :: Python :: Implementation :: PyPy
17
16
  Classifier: Operating System :: POSIX
18
17
  Classifier: Operating System :: Microsoft :: Windows
19
- Requires-Python: >=3.7
20
- Provides-Extra: cext
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
21
20
  License-File: COPYING
21
+ Provides-Extra: cext
22
+ Requires-Dist: cython>=0.29; extra == "cext"
23
+ Provides-Extra: dev
24
+ Requires-Dist: ruff==0.4.3; extra == "dev"
22
25
 
23
26
  fastbencode
24
27
  ===========
@@ -38,6 +41,12 @@ Example:
38
41
  >>> bdecode(bencode([1, 2, b'a', {b'd': 3}]))
39
42
  [1, 2, b'a', {b'd': 3}]
40
43
 
44
+ The default ``bencode``/``bdecode`` functions just operate on
45
+ bytestrings. Use ``bencode_utf8`` / ``bdecode_utf8`` to
46
+ serialize/deserialize all plain strings as UTF-8 bytestrings.
47
+ Note that for performance reasons, all dictionary keys still have to be
48
+ bytestrings.
49
+
41
50
  License
42
51
  =======
43
52
  fastbencode is available under the GNU GPL, version 2 or later.
@@ -1,16 +1,8 @@
1
- .gitignore
2
- CODE_OF_CONDUCT.md
3
1
  COPYING
4
2
  MANIFEST.in
5
3
  README.md
6
- SECURITY.md
7
- disperse.conf
8
4
  pyproject.toml
9
- setup.cfg
10
5
  setup.py
11
- .github/workflows/disperse.yml
12
- .github/workflows/pythonpackage.yml
13
- .github/workflows/pythonpublish.yml
14
6
  fastbencode/__init__.py
15
7
  fastbencode/_bencode_py.py
16
8
  fastbencode/_bencode_pyx.h
@@ -1,3 +1,6 @@
1
1
 
2
2
  [cext]
3
3
  cython>=0.29
4
+
5
+ [dev]
6
+ ruff==0.4.3
@@ -0,0 +1,75 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=61.2",
4
+ "cython>=0.29",
5
+ ]
6
+ build-backend = "setuptools.build_meta"
7
+
8
+ [project]
9
+ name = "fastbencode"
10
+ description = "Implementation of bencode with optional fast C extensions"
11
+ maintainers = [{name = "Breezy Developers", email = "breezy-core@googlegroups.com"}]
12
+ readme = "README.md"
13
+ license = {text = "GPLv2 or later"}
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3.8",
16
+ "Programming Language :: Python :: 3.9",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: Implementation :: CPython",
21
+ "Programming Language :: Python :: Implementation :: PyPy",
22
+ "Operating System :: POSIX",
23
+ "Operating System :: Microsoft :: Windows",
24
+ ]
25
+ requires-python = ">=3.8"
26
+ dynamic = ["version"]
27
+ dependencies = []
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/breezy-team/fastbencode"
31
+ GitHub = "https://github.com/breezy-team/fastbencode"
32
+
33
+ [project.optional-dependencies]
34
+ cext = ["cython>=0.29"]
35
+ dev = [
36
+ "ruff==0.4.3"
37
+ ]
38
+
39
+ [tool.setuptools]
40
+ packages = ["fastbencode"]
41
+ include-package-data = false
42
+
43
+ [tool.setuptools.dynamic]
44
+ version = {attr = "fastbencode.__version__"}
45
+
46
+ [tool.ruff]
47
+ target-version = "py37"
48
+ line-length = 79
49
+
50
+ [tool.ruff.lint]
51
+ select = [
52
+ "ANN",
53
+ "D",
54
+ "E",
55
+ "F",
56
+ "I",
57
+ "UP",
58
+ ]
59
+ ignore = [
60
+ "ANN001",
61
+ "ANN002",
62
+ "ANN101",
63
+ "ANN201",
64
+ "ANN202",
65
+ "ANN204",
66
+ "D100",
67
+ "D101",
68
+ "D102",
69
+ "D103",
70
+ "D105",
71
+ "D107",
72
+ ]
73
+
74
+ [tool.ruff.lint.pydocstyle]
75
+ convention = "google"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -2,15 +2,17 @@
2
2
 
3
3
  import os
4
4
  import sys
5
- from setuptools import setup, Extension
5
+
6
+ from setuptools import Extension, setup
7
+
6
8
  try:
7
9
  from packaging.version import Version
8
10
  except ImportError:
9
11
  from distutils.version import LooseVersion as Version
10
12
 
11
13
  try:
12
- from Cython.Distutils import build_ext
13
14
  from Cython.Compiler.Version import version as cython_version
15
+ from Cython.Distutils import build_ext
14
16
  except ImportError:
15
17
  have_cython = False
16
18
  # try to build the extension from the prior generated source.
@@ -26,8 +28,8 @@ else:
26
28
  cython_version_info = Version(cython_version)
27
29
  if cython_version_info < Version(minimum_cython_version):
28
30
  print("Version of Cython is too old. "
29
- "Current is %s, need at least %s."
30
- % (cython_version, minimum_cython_version))
31
+ f"Current is {cython_version}, "
32
+ f"need at least {minimum_cython_version}.")
31
33
  print("If the .c files are available, they will be built,"
32
34
  " but modifying the .pyx files will not rebuild them.")
33
35
  have_cython = False
@@ -1,24 +0,0 @@
1
- ---
2
- name: Disperse configuration
3
-
4
- "on":
5
- - push
6
-
7
- jobs:
8
- build:
9
-
10
- runs-on: ubuntu-latest
11
-
12
- steps:
13
- - uses: actions/checkout@v2
14
- - name: Set up Python
15
- uses: actions/setup-python@v2
16
- - name: Install dependencies
17
- run: |
18
- sudo apt install protobuf-compiler
19
- - name: Install disperse
20
- run: |
21
- pip install git+https://github.com/jelmer/disperse
22
- - name: Validate disperse.conf
23
- run: |
24
- PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python disperse validate .
@@ -1,32 +0,0 @@
1
- name: Python package
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- build:
7
-
8
- runs-on: ${{ matrix.os }}
9
- strategy:
10
- matrix:
11
- os: [ubuntu-latest, macos-latest, windows-latest]
12
- python-version: [3.7, 3.8, 3.9, '3.10', '3.11']
13
- fail-fast: false
14
-
15
- steps:
16
- - uses: actions/checkout@v2
17
- - name: Set up Python ${{ matrix.python-version }}
18
- uses: actions/setup-python@v2
19
- with:
20
- python-version: ${{ matrix.python-version }}
21
- - name: Install dependencies
22
- run: |
23
- python -m pip install --upgrade pip cython
24
- pip install -U pip flake8
25
- - name: Style checks
26
- run: |
27
- python -m flake8
28
- - name: Test suite run
29
- run: |
30
- python -m unittest fastbencode.tests.test_suite
31
- env:
32
- PYTHONHASHSEED: random
@@ -1,58 +0,0 @@
1
- ---
2
- name: Build Python Wheels
3
-
4
- "on":
5
- push:
6
-
7
- jobs:
8
- build:
9
- runs-on: ${{ matrix.os }}
10
- strategy:
11
- matrix:
12
- os: [ubuntu-latest, macos-latest, windows-latest]
13
- fail-fast: true
14
-
15
- steps:
16
- - uses: actions/checkout@v3
17
- - uses: actions/setup-python@v3
18
- - name: Install dependencies
19
- run: |
20
- python -m pip install --upgrade pip cython
21
- pip install setuptools wheel cibuildwheel
22
- - name: Run test suite
23
- run: python -m unittest fastbencode.tests.test_suite
24
- - name: Set up QEMU
25
- uses: docker/setup-qemu-action@v1
26
- if: "matrix.os == 'ubuntu-latest'"
27
- - name: Build wheels
28
- run: python -m cibuildwheel --output-dir wheelhouse
29
- env:
30
- CIBW_ARCHS_LINUX: x86_64 aarch64
31
- CIBW_ARCHS_MACOS: x86_64 arm64 universal2
32
- CIBW_ARCHS_WINDOWS: AMD64 x86
33
- - name: Upload wheels
34
- uses: actions/upload-artifact@v3
35
- with:
36
- path: ./wheelhouse/*.whl
37
-
38
- publish:
39
- runs-on: ubuntu-latest
40
-
41
- needs: build
42
- if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
43
- steps:
44
- - uses: actions/setup-python@v3
45
-
46
- - name: Install twine
47
- run: |
48
- python -m pip install --upgrade pip
49
- pip install twine
50
- - name: Download wheels
51
- uses: actions/download-artifact@v2
52
- - name: Display structure of downloaded files
53
- run: ls -R
54
- - name: Publish wheels
55
- env:
56
- TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
57
- TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
58
- run: twine upload artifact/*.whl
@@ -1,8 +0,0 @@
1
- *.so
2
- build
3
- fastbencode/_bencode_pyx.c
4
- fastbencode/_bencode_pyx.h
5
- __pycache__
6
- fastbencode.egg-info
7
- *.pyc
8
- dist
@@ -1,76 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, sex characteristics, gender identity and expression,
9
- level of experience, education, socio-economic status, nationality, personal
10
- appearance, race, religion, or sexual identity and orientation.
11
-
12
- ## Our Standards
13
-
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
16
-
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
22
-
23
- Examples of unacceptable behavior by participants include:
24
-
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
28
- * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
31
- * Other conduct which could reasonably be considered inappropriate in a
32
- professional setting
33
-
34
- ## Our Responsibilities
35
-
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
39
-
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
45
-
46
- ## Scope
47
-
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
-
55
- ## Enforcement
56
-
57
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at core@breezy-vcs.org. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
-
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
67
-
68
- ## Attribution
69
-
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72
-
73
- [homepage]: https://www.contributor-covenant.org
74
-
75
- For answers to common questions about this code of conduct, see
76
- https://www.contributor-covenant.org/faq
@@ -1,5 +0,0 @@
1
- # Security Policy
2
-
3
- ## Reporting a Vulnerability
4
-
5
- Please report security issues by e-mail to core@breezy-vcs.org.
@@ -1,8 +0,0 @@
1
- # See https://github.com/jelmer/disperse
2
- timeout_days: 5
3
- tag_name: "v$VERSION"
4
- verify_command: "python3 -m unittest fastbencode.tests.test_suite"
5
- update_version {
6
- path: "fastbencode/__init__.py"
7
- new_line: '__version__ = $TUPLED_VERSION'
8
- }
@@ -1,162 +0,0 @@
1
- # bencode structured encoding
2
- #
3
- # Written by Petru Paler
4
- #
5
- # Permission is hereby granted, free of charge, to any person
6
- # obtaining a copy of this software and associated documentation files
7
- # (the "Software"), to deal in the Software without restriction,
8
- # including without limitation the rights to use, copy, modify, merge,
9
- # publish, distribute, sublicense, and/or sell copies of the Software,
10
- # and to permit persons to whom the Software is furnished to do so,
11
- # subject to the following conditions:
12
- #
13
- # The above copyright notice and this permission notice shall be
14
- # included in all copies or substantial portions of the Software.
15
- #
16
- # Modifications copyright (C) 2008 Canonical Ltd
17
-
18
-
19
- from typing import Dict, Type, Callable, List
20
-
21
-
22
- class BDecoder(object):
23
-
24
- def __init__(self, yield_tuples=False):
25
- """Constructor.
26
-
27
- :param yield_tuples: if true, decode "l" elements as tuples rather than
28
- lists.
29
- """
30
- self.yield_tuples = yield_tuples
31
- decode_func = {}
32
- decode_func[b'l'] = self.decode_list
33
- decode_func[b'd'] = self.decode_dict
34
- decode_func[b'i'] = self.decode_int
35
- decode_func[b'0'] = self.decode_string
36
- decode_func[b'1'] = self.decode_string
37
- decode_func[b'2'] = self.decode_string
38
- decode_func[b'3'] = self.decode_string
39
- decode_func[b'4'] = self.decode_string
40
- decode_func[b'5'] = self.decode_string
41
- decode_func[b'6'] = self.decode_string
42
- decode_func[b'7'] = self.decode_string
43
- decode_func[b'8'] = self.decode_string
44
- decode_func[b'9'] = self.decode_string
45
- self.decode_func = decode_func
46
-
47
- def decode_int(self, x, f):
48
- f += 1
49
- newf = x.index(b'e', f)
50
- n = int(x[f:newf])
51
- if x[f:f + 2] == b'-0':
52
- raise ValueError
53
- elif x[f:f + 1] == b'0' and newf != f + 1:
54
- raise ValueError
55
- return (n, newf + 1)
56
-
57
- def decode_string(self, x, f):
58
- colon = x.index(b':', f)
59
- n = int(x[f:colon])
60
- if x[f:f + 1] == b'0' and colon != f + 1:
61
- raise ValueError
62
- colon += 1
63
- return (x[colon:colon + n], colon + n)
64
-
65
- def decode_list(self, x, f):
66
- r, f = [], f + 1
67
- while x[f:f + 1] != b'e':
68
- v, f = self.decode_func[x[f:f + 1]](x, f)
69
- r.append(v)
70
- if self.yield_tuples:
71
- r = tuple(r)
72
- return (r, f + 1)
73
-
74
- def decode_dict(self, x, f):
75
- r, f = {}, f + 1
76
- lastkey = None
77
- while x[f:f + 1] != b'e':
78
- k, f = self.decode_string(x, f)
79
- if lastkey is not None and lastkey >= k:
80
- raise ValueError
81
- lastkey = k
82
- r[k], f = self.decode_func[x[f:f + 1]](x, f)
83
- return (r, f + 1)
84
-
85
- def bdecode(self, x):
86
- if not isinstance(x, bytes):
87
- raise TypeError
88
- try:
89
- r, l = self.decode_func[x[:1]](x, 0) # noqa: E741
90
- except (IndexError, KeyError, OverflowError) as e:
91
- raise ValueError(str(e))
92
- if l != len(x): # noqa: E741
93
- raise ValueError
94
- return r
95
-
96
-
97
- _decoder = BDecoder()
98
- bdecode = _decoder.bdecode
99
-
100
- _tuple_decoder = BDecoder(True)
101
- bdecode_as_tuple = _tuple_decoder.bdecode
102
-
103
-
104
- class Bencached(object):
105
- __slots__ = ['bencoded']
106
-
107
- def __init__(self, s):
108
- self.bencoded = s
109
-
110
-
111
- def encode_bencached(x, r):
112
- r.append(x.bencoded)
113
-
114
-
115
- def encode_bool(x, r):
116
- encode_int(int(x), r)
117
-
118
-
119
- def encode_int(x, r):
120
- r.extend((b'i', int_to_bytes(x), b'e'))
121
-
122
-
123
- def encode_string(x, r):
124
- r.extend((int_to_bytes(len(x)), b':', x))
125
-
126
-
127
- def encode_list(x, r):
128
- r.append(b'l')
129
- for i in x:
130
- encode_func[type(i)](i, r)
131
- r.append(b'e')
132
-
133
-
134
- def encode_dict(x, r):
135
- r.append(b'd')
136
- ilist = sorted(x.items())
137
- for k, v in ilist:
138
- r.extend((int_to_bytes(len(k)), b':', k))
139
- encode_func[type(v)](v, r)
140
- r.append(b'e')
141
-
142
-
143
- encode_func: Dict[Type, Callable[[object, List[bytes]], None]] = {}
144
- encode_func[type(Bencached(0))] = encode_bencached
145
- encode_func[int] = encode_int
146
-
147
-
148
- def int_to_bytes(n):
149
- return b'%d' % n
150
-
151
-
152
- encode_func[bytes] = encode_string
153
- encode_func[list] = encode_list
154
- encode_func[tuple] = encode_list
155
- encode_func[dict] = encode_dict
156
- encode_func[bool] = encode_bool
157
-
158
-
159
- def bencode(x):
160
- r = []
161
- encode_func[type(x)](x, r)
162
- return b''.join(r)
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools", "cython>=0.29", "packaging"]
3
- build-backend = "setuptools.build_meta"
fastbencode-0.2/setup.cfg DELETED
@@ -1,35 +0,0 @@
1
- [metadata]
2
- name = fastbencode
3
- description = Implementation of bencode with optional fast C extensions
4
- maintainer = Breezy Developers
5
- maintainer_email = breezy-core@googlegroups.com
6
- url = https://github.com/breezy-team/fastbencode
7
- long_description = file:README.md
8
- license = GPLv2 or later
9
- version = attr:fastbencode.__version__
10
- project_urls =
11
- GitHub = https://github.com/breezy-team/fastbencode
12
- classifiers =
13
- Programming Language :: Python :: 3.7
14
- Programming Language :: Python :: 3.8
15
- Programming Language :: Python :: 3.9
16
- Programming Language :: Python :: 3.10
17
- Programming Language :: Python :: 3.11
18
- Programming Language :: Python :: Implementation :: CPython
19
- Programming Language :: Python :: Implementation :: PyPy
20
- Operating System :: POSIX
21
- Operating System :: Microsoft :: Windows
22
-
23
- [options]
24
- python_requires = >=3.7
25
- packages = fastbencode
26
- setup_requires =
27
- cython>=0.29
28
-
29
- [options.extras_require]
30
- cext = cython>=0.29
31
-
32
- [egg_info]
33
- tag_build =
34
- tag_date = 0
35
-
File without changes
File without changes