google / adb-sync

Apache License 2.0
1.08k stars 171 forks source link

Problems with parameters: a bytes-like object is required, not 'str' #37

Open marcoseifert opened 5 years ago

marcoseifert commented 5 years ago

Hello, when starting adb-sync I always get the following error message. I'm using Windows 10, Python 3.7.2 and the latest version of the script.

C:\Users\Marco>adb-sync --reverse /sdcard/DCIM/Camera ~/Downloads INFO:root:Sync: local b'~/Downloads/Camera', remote b'/sdcard/DCIM/Camera' Traceback (most recent call last): File "C:\Users\Marco\Desktop\adb-sync-master\adb-sync.py", line 883, in <module> main() File "C:\Users\Marco\Desktop\adb-sync-master\adb-sync.py", line 870, in main if not syncer.IsWorking(): File "C:\Users\Marco\Desktop\adb-sync-master\adb-sync.py", line 507, in IsWorking return self.adb.IsWorking() File "C:\Users\Marco\Desktop\adb-sync-master\adb-sync.py", line 221, in IsWorking b'date +%s' % (self.QuoteArgument(test_string),)]) as stdout: File "C:\Users\Marco\Desktop\adb-sync-master\adb-sync.py", line 76, in __init__ self.popen = subprocess.Popen(args, stdout=subprocess.PIPE) File "C:\Users\Marco\AppData\Local\Programs\Python\Python37\lib\subprocess.py", line 775, in __init__ restore_signals, start_new_session) File "C:\Users\Marco\AppData\Local\Programs\Python\Python37\lib\subprocess.py", line 1119, in _execute_child args = list2cmdline(args) File "C:\Users\Marco\AppData\Local\Programs\Python\Python37\lib\subprocess.py", line 530, in list2cmdline needquote = (" " in arg) or ("\t" in arg) or not arg TypeError: a bytes-like object is required, not 'str'

I saw under Issue #10 that chaning the script could solve this problem but I'm not familiar with Python at all. I also tried different Python versions but no success. Are there any other solutions or will this problem be fixed in the future? I love this tool and I would be happy to use it again.

Thanks in advance!

carlosmax3D commented 5 years ago

Hi. One line above this file C:\Users\Marco\AppData\Local\Programs\Python\Python37\lib\subprocess.py, line 529, add this line "arg = arg.decode() if type(arg)==bytes else arg" without quotes and in file adb-sync line 214 change and add this lines: s = '(; #ls$PATH\'"(\\\\){};!\xc0\xaf\xff\xc2\xbf' test_strings = [ b'(', s.encode('utf-8') ] After that, it works like a charm.

Test it.

Venryx commented 5 years ago

I followed the instructions above, and eventually got it working. However, the instructions are a little unclear, so I thought I'd help clarify.

For the first step, the line above which you insert the new line is indeed line 529 at the time of this writing (v3.7.4). To prevent a misalignment from an update however, this is the actual content of line 529 above which to insert: needquote = (" " in arg) or ("\t" in arg) or not arg (technically this is line 530, but line 529 is an empty line, so just put the new line above one of them)

For the second step, the new code was not formatted properly as a code block, meaning when I copy and paste it in it's invalid. Instead of trying to figure out how Github mangled the string, I just added a "return true" line at the start of that "IsWorking()" function.

kennethso168 commented 5 years ago

I think it is a bad idea to directly edit the files in the python standard library. It might break other python scripts/programmes using that function. Also, you will need to edit it every time when python is updated.

In fact, python allows you to replace functions on the fly (also called monkey-patching). So you can change the behaviour of the relevant function in the subprocess module within adb-sync itself without modifying the file in standard library.

Just add the code after all the from ... import ... lines after line 28 of the adb-sync file like the following (code copied from standard library file with adjustments made):

... # code before
from typing import Callable, cast, Dict, List, IO, Iterable, Optional, Tuple, Type

def list2cmdline_patch(seq):
    """
    Translate a sequence of arguments into a command line
    string, using the same rules as the MS C runtime:

    1) Arguments are delimited by white space, which is either a
       space or a tab.

    2) A string surrounded by double quotation marks is
       interpreted as a single argument, regardless of white space
       contained within.  A quoted string can be embedded in an
       argument.

    3) A double quotation mark preceded by a backslash is
       interpreted as a literal double quotation mark.

    4) Backslashes are interpreted literally, unless they
       immediately precede a double quotation mark.

    5) If backslashes immediately precede a double quotation mark,
       every pair of backslashes is interpreted as a literal
       backslash.  If the number of backslashes is odd, the last
       backslash escapes the next double quotation mark as
       described in rule 3.
    """

    # See
    # http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
    # or search http://msdn.microsoft.com for
    # "Parsing C++ Command-Line Arguments"
    result = []
    needquote = False
    for arg in seq:
        bs_buf = []

        # Add a space to separate this argument from the others
        if result:
            result.append(' ')
        arg = arg.decode() if type(arg)==bytes else arg
        needquote = (" " in arg) or ("\t" in arg) or not arg
        if needquote:
            result.append('"')

        for c in arg:
            if c == '\\':
                # Don't know if we need to double yet.
                bs_buf.append(c)
            elif c == '"':
                # Double backslashes.
                result.append('\\' * len(bs_buf)*2)
                bs_buf = []
                result.append('\\"')
            else:
                # Normal char
                if bs_buf:
                    result.extend(bs_buf)
                    bs_buf = []
                result.append(c)

        # Add remaining backslashes, if any.
        if bs_buf:
            result.extend(bs_buf)

        if needquote:
            result.extend(bs_buf)
            result.append('"')

    return ''.join(result)

subprocess.list2cmdline = list2cmdline_patch

class OSLike(object):
... # code after

And change the isWorking() function as below:

  def IsWorking(self) -> bool:
    """Tests the adb connection."""
    # This string should contain all possible evil, but no percent signs.
    # Note this code uses 'date' and not 'echo', as date just calls strftime
    # while echo does its own backslash escape handling additionally to the
    # shell's. Too bad printf "%s\n" is not available.
    s = '(;  #`ls`$PATH\'"(\\\\\\\\){};!\xc0\xaf\xff\xc2\xbf'
    test_strings = [
        b'(', s.encode('utf-8')
    ]
    for test_string in test_strings:
      good = False
...