GithubHelp home page GithubHelp logo

bcode's Introduction

bcoding

yet another… but mine is fast as hell.

install

pip install bcoding

use

from bcoding import bencode, bdecode

decoding:

# decoding from binary files or streams:
with open('some.torrent', 'rb') as f:
    torrent = bdecode(f)
    print(torrent['announce'])

# decoding from (byte)strings:
one = bdecode(b'i1e')
two = bdecode('3:two')

encoding (note that any iterable or mapping can be bencoded):

# encoding into binary files or streams:
bencode({'a': 0}, sys.stdout.buffer) # ⇒ d1:ai0ee

# encoding to bytestrings:
assert bencode(('a', 0)) == b'l1:ai0ee'

bcode's People

Contributors

alex avatar flying-sheep avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

bcode's Issues

Is there a way to disable decoding bytes into a string?

There are fields that are bytes encoded as string, like token, peer id, node id, etc. But actually they are better treated as bytes instead of str. And the decode of these values are just a guess. See bcoding.py#L76. It's better to handle a certain type rather than do a check of type before dealing with the data.

So, is there a way to disable the decoding?

def _decode_buffer(f):
	"""
	String types are normal (byte)strings
	starting with an integer followed by ':'
	which designates the string’s length.
	
	Since there’s no way to specify the byte type
	in bencoded files, we have to guess
	"""
	strlen = int(_readuntil(f, _TYPE_SEP))
	buf = f.read(strlen)
	if not len(buf) == strlen:
		raise ValueError(
			'string expected to be {} bytes long but the file ended after {} bytes'
			.format(strlen, len(buf)))
	try:
		return buf.decode()
	except UnicodeDecodeError:
        return buf

Decoding from strings and bytestrings does not work

BytesIO doesn't have a peek method. I'm using the 1.0 version from PyPI.

(bcodeproblem)cal@curry:~/src$ python
Python 2.7.3 (default, Sep 26 2012, 21:51:14) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from bcoding import bencode, bdecode
>>> one = bdecode(b'i1e')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/cal/src/bcodeproblem/local/lib/python2.7/site-packages/bcoding.py", line 118, in bdecode
    first_byte = f_or_data.peek(1)[:1]
AttributeError: '_io.BytesIO' object has no attribute 'peek'
>>> two = bdecode('3:two')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/cal/src/bcodeproblem/local/lib/python2.7/site-packages/bcoding.py", line 118, in bdecode
    first_byte = f_or_data.peek(1)[:1]
AttributeError: '_io.BytesIO' object has no attribute 'peek'

Test fails on python2

[mdevaev@reki(g:master) g:/]$ python2 -V
Python 2.7.5
[mdevaev@reki(g:master) g:/]$ python2 test.py 
=========================================================================== test session starts ============================================================================
platform linux2 -- Python 2.7.5 -- pytest-2.4.2
collected 8 items 

test.py FF...F..

================================================================================= FAILURES =================================================================================
___________________________________________________________________________ test_stream_decoding ___________________________________________________________________________

    def test_stream_decoding():
        with BytesIO(b'd2:hii1ee') as f:
>               mapping = bdecode(f)

test.py:8: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f_or_data = <_io.BytesIO object at 0x1e18c50>

    def bdecode(f_or_data):
        """
        bdecodes data by looking up the type byte,
        and using it to look up the respective decoding function,
        which in turn is used to return the decoded object

        The parameter can be a file opened in bytes mode,
        bytes or a string (the last of which will be decoded)
        """
        if isinstance(f_or_data, str):
                f_or_data = f_or_data.encode()
        if isinstance(f_or_data, bytes):
                f_or_data = BytesIO(f_or_data)

        #TODO: the following like is the only one that needs readahead.
        #peek returns a arbitrary amount of bytes, so we have to slice.
        if f_or_data.seekable():
                first_byte = f_or_data.read(1)
                f_or_data.seek(-1, SEEK_CUR)
        else:
                first_byte = f_or_data.peek(1)[:1]
        btype = TYPES.get(first_byte)
        if btype is not None:
>               return btype(f_or_data)

bcoding.py:126: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f = <_io.BytesIO object at 0x1e18c50>

    def _decode_dict(f):
        assert_btype(f.read(1), _TYPE_DICT)
        ret = {}
>       key = bdecode(f)

bcoding.py:86: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f_or_data = <_io.BytesIO object at 0x1e18c50>

    def bdecode(f_or_data):
        """
        bdecodes data by looking up the type byte,
        and using it to look up the respective decoding function,
        which in turn is used to return the decoded object

        The parameter can be a file opened in bytes mode,
        bytes or a string (the last of which will be decoded)
        """
        if isinstance(f_or_data, str):
                f_or_data = f_or_data.encode()
        if isinstance(f_or_data, bytes):
                f_or_data = BytesIO(f_or_data)

        #TODO: the following like is the only one that needs readahead.
        #peek returns a arbitrary amount of bytes, so we have to slice.
        if f_or_data.seekable():
                first_byte = f_or_data.read(1)
                f_or_data.seek(-1, SEEK_CUR)
        else:
                first_byte = f_or_data.peek(1)[:1]
        btype = TYPES.get(first_byte)
        if btype is not None:
                return btype(f_or_data)
        else: #Used in dicts and lists to designate an end
>               assert_btype(f_or_data.read(1), _TYPE_END)

bcoding.py:128: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

byte = '2', typ = 'e'

    def assert_btype(byte, typ):
        if not byte == typ:
                raise TypeError(
                        'Tried to decode type {!r} with identifier {!r} but got identifier {!r} instead'
>                       .format(TYPES[typ] or 'End', typ, byte))
E     TypeError: Tried to decode type 'End' with identifier 'e' but got identifier '2' instead

bcoding.py:29: TypeError
___________________________________________________________________________ test_buffer_decoding ___________________________________________________________________________

    def test_buffer_decoding():
>       assert bdecode(b'3:one') == 'one'

test.py:12: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f_or_data = <_io.BytesIO object at 0x1e18f50>

    def bdecode(f_or_data):
        """
        bdecodes data by looking up the type byte,
        and using it to look up the respective decoding function,
        which in turn is used to return the decoded object

        The parameter can be a file opened in bytes mode,
        bytes or a string (the last of which will be decoded)
        """
        if isinstance(f_or_data, str):
                f_or_data = f_or_data.encode()
        if isinstance(f_or_data, bytes):
                f_or_data = BytesIO(f_or_data)

        #TODO: the following like is the only one that needs readahead.
        #peek returns a arbitrary amount of bytes, so we have to slice.
        if f_or_data.seekable():
                first_byte = f_or_data.read(1)
                f_or_data.seek(-1, SEEK_CUR)
        else:
                first_byte = f_or_data.peek(1)[:1]
        btype = TYPES.get(first_byte)
        if btype is not None:
                return btype(f_or_data)
        else: #Used in dicts and lists to designate an end
>               assert_btype(f_or_data.read(1), _TYPE_END)

bcoding.py:128: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

byte = '3', typ = 'e'

    def assert_btype(byte, typ):
        if not byte == typ:
                raise TypeError(
                        'Tried to decode type {!r} with identifier {!r} but got identifier {!r} instead'
>                       .format(TYPES[typ] or 'End', typ, byte))
E     TypeError: Tried to decode type 'End' with identifier 'e' but got identifier '3' instead

bcoding.py:29: TypeError
______________________________________________________________________ test_decode_incomplete_buffer _______________________________________________________________________

    def test_decode_incomplete_buffer():
        with raises(ValueError):
>               bdecode('1:')

test.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

f_or_data = <_io.BytesIO object at 0x1ca3470>

    def bdecode(f_or_data):
        """
        bdecodes data by looking up the type byte,
        and using it to look up the respective decoding function,
        which in turn is used to return the decoded object

        The parameter can be a file opened in bytes mode,
        bytes or a string (the last of which will be decoded)
        """
        if isinstance(f_or_data, str):
                f_or_data = f_or_data.encode()
        if isinstance(f_or_data, bytes):
                f_or_data = BytesIO(f_or_data)

        #TODO: the following like is the only one that needs readahead.
        #peek returns a arbitrary amount of bytes, so we have to slice.
        if f_or_data.seekable():
                first_byte = f_or_data.read(1)
                f_or_data.seek(-1, SEEK_CUR)
        else:
                first_byte = f_or_data.peek(1)[:1]
        btype = TYPES.get(first_byte)
        if btype is not None:
                return btype(f_or_data)
        else: #Used in dicts and lists to designate an end
>               assert_btype(f_or_data.read(1), _TYPE_END)

bcoding.py:128: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

byte = '1', typ = 'e'

    def assert_btype(byte, typ):
        if not byte == typ:
                raise TypeError(
                        'Tried to decode type {!r} with identifier {!r} but got identifier {!r} instead'
>                       .format(TYPES[typ] or 'End', typ, byte))
E     TypeError: Tried to decode type 'End' with identifier 'e' but got identifier '1' instead

bcoding.py:29: TypeError
==================================================================== 3 failed, 5 passed in 0.03 seconds ====================================================================

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.