Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: a bytes-like object is required, not 'str' #29

Closed
CyberDefend3r opened this issue Nov 18, 2019 · 10 comments
Closed

TypeError: a bytes-like object is required, not 'str' #29

CyberDefend3r opened this issue Nov 18, 2019 · 10 comments

Comments

@CyberDefend3r
Copy link

I am trying to do something super simple. Well that I thought was simple. I spent like 20 min writing the code and about 2 hours trying to figure out why it doesn't work...

I keep getting a typeError from subprocess called from the exiftool start(). I am sure I am doing something wrong but I cant figure it out. I thought i would post here on the very slim chance this is a bug and not my fault.

"""
Used to take pictures seperated into folders based on date like year/month/photo.jpg, and 
change the create date of photo to the year and month from the folder names.

The script assumes that exiftool.exe and script are in the same directory. year folders are 
in same dir as well.
"""

import os
from glob import glob
import exiftool

class collection:
    def __init__(self):
        self.dateFile = []
        self.fileList = {}
        self.paths = glob('**/**')
        self.curDir = os.getcwd()
        self.exifTool = os.path.join(self.curDir.encode(), 'exiftool.exe'.encode())

    def setVariables(self):
        for path in self.paths:
            globPath = os.path.join(path, '*.*')
            picturPath = glob(globPath)
            pictures = []
            for pic in picturPath:
                pictures.append(os.path.join(self.curDir, pic))
            date = str.replace(path, '\\', ':') + ':01 00:01:01'
            self.fileList = {'date':date, 'pictures':pictures}
            self.dateFile.append(self.fileList)

class changeDate:
    def __init__(self, collection):
        self.dateFile = collection.dateFile
        self.exifTool = exiftool.fsencode(collection.exifTool)

    def buildEXIFcommand(self):
        for filelist in self.dateFile:
            date = filelist['date']
            for picfile in filelist['pictures']:
                modDate = '"' + '-FileCreateDate=' + date + '"'
                modDate.encode()
                picfile = exiftool.fsencode(picfile)
                self.exifChange(modDate, picfile)

    def exifChange(self, modDate, picfile):
        with exiftool.ExifTool(self.exifTool) as et:
            et.execute(modDate, picfile)

if __name__ == "__main__":
    Collection = collection()
    Collection.setVariables()
    ChangeDate = changeDate(Collection)
    ChangeDate.buildEXIFcommand()

Traceback (most recent call last):
  File "c:\Users\trevo\.vscode\extensions\ms-python.python-2019.10.44104\pythonFiles\ptvsd_launcher.py", line 43, in <module>
    main(ptvsdArgs)
  File "c:\Users\trevo\.vscode\extensions\ms-python.python-2019.10.44104\pythonFiles\lib\python\old_ptvsd\ptvsd\__main__.py", line 432, in main
    run()
  File "c:\Users\trevo\.vscode\extensions\ms-python.python-2019.10.44104\pythonFiles\lib\python\old_ptvsd\ptvsd\__main__.py", line 316, in run_file
    runpy.run_path(target, run_name='__main__')
  File "C:\Users\trevo\AppData\Local\Programs\Python\Python37-32\lib\runpy.py", line 263, in run_path
    pkg_name=pkg_name, script_name=fname)
  File "C:\Users\trevo\AppData\Local\Programs\Python\Python37-32\lib\runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
  File "C:\Users\trevo\AppData\Local\Programs\Python\Python37-32\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "c:\Users\trevo\OneDrive\Desktop\pics\datchange.py", line 46, in <module>
    ChangeDate.buildEXIFcommand()
  File "c:\Users\trevo\OneDrive\Desktop\pics\datchange.py", line 36, in buildEXIFcommand
    self.exifChange(modDate, picfile)
  File "c:\Users\trevo\OneDrive\Desktop\pics\datchange.py", line 39, in exifChange
    with exiftool.ExifTool(self.exifTool) as et:
  File "C:\Users\trevo\AppData\Local\Programs\Python\Python37-32\lib\site-packages\exiftool.py", line 192, in __enter__
    self.start()
  File "C:\Users\trevo\AppData\Local\Programs\Python\Python37-32\lib\site-packages\exiftool.py", line 175, in start
    stderr=devnull)
  File "C:\Users\trevo\AppData\Local\Programs\Python\Python37-32\lib\subprocess.py", line 775, in __init__
    restore_signals, start_new_session)
  File "C:\Users\trevo\AppData\Local\Programs\Python\Python37-32\lib\subprocess.py", line 1119, in _execute_child
    args = list2cmdline(args)
  File "C:\Users\trevo\AppData\Local\Programs\Python\Python37-32\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'
@jwag956
Copy link

jwag956 commented Nov 18, 2019

Looks like the problem is that 'modData.encode()' RETURNS an encoded string - it doesn't modify modDate - so you are still passing in a str.

@CyberDefend3r
Copy link
Author

Looks like the problem is that 'modData.encode()' RETURNS an encoded string - it doesn't modify modDate - so you are still passing in a str.

Thank you for pointing this out.
Seems that no matter what i do to it it stays a string... Do you have any suggestions?

@jwag956
Copy link

jwag956 commented Nov 18, 2019 via email

@CyberDefend3r
Copy link
Author

CyberDefend3r commented Nov 18, 2019

modDataBytes = modData.encode() should work fine

Same issue.

    def buildEXIFcommand(self):
        for filelist in self.dateFile:
            date = filelist['date']
            for picfile in filelist['pictures']:
                modDate = '"' + '-FileCreateDate=' + date + '"'
                modDateBytes = modDate.encode()
                picfile = exiftool.fsencode(picfile)
                self.exifChange(modDateBytes, picfile)

    def exifChange(self, modDateBytes, picfile):
        with exiftool.ExifTool(self.exifTool) as et:
            et.execute(modDateBytes, picfile)

@jwag956
Copy link

jwag956 commented Nov 18, 2019

Goodpoint - looking closer - it is failing at the call to 'start' (which is run as part of the 'with' context manager) - so not even looking at your parameters yet.

Looking at the error in subprocess - (which is windows specific alas) - it appears that it (error message not withstanding) it wants a string - not bytes. And as I said before - this is on the start - not your 'execute' command - so that means it must be an issue with your executable. So I would try creating your path WITHOUT .encode() e.g. :

os.path.join(self.curDir, 'exiftool.exe')

lets see what that brings.

@CyberDefend3r
Copy link
Author

Goodpoint - looking closer - it is failing at the call to 'start' (which is run as part of the 'with' context manager) - so not even looking at your parameters yet.

Looking at the error in subprocess - (which is windows specific alas) - it appears that it (error message not withstanding) it wants a string - not bytes. And as I said before - this is on the start - not your 'execute' command - so that means it must be an issue with your executable. So I would try creating your path WITHOUT .encode() e.g. :

os.path.join(self.curDir, 'exiftool.exe')

lets see what that brings.

I get the same traceback

I can confirm all three variables passed to exiftool are bytes objects
when printed:

b'C:\\Users\\trevo\\OneDrive\\Desktop\\pics\\exiftool.exe'
b'-FileCreateDate=2018:06:01 00:01:01'
b'C:\\Users\\trevo\\OneDrive\\Desktop\\pics\\2018\\06\\MVIMG_20190719_185858.jpg'

Thanks for the help!

@jwag956
Copy link

jwag956 commented Nov 18, 2019

That is my point. Your path to exiftool executable should NOT BE BYTES.

When you use the 'with' stmt - it ends up calling:

self._process = subprocess.Popen( [self.executable, "-stay_open", "True", "-@", "-", "-common_args", "-G", "-n"],

Where self.executable is what you called exifTool's constructor with. Popen takes strings.

However - when you call 'execute(...)' - it is writing to a pipe - and that required bytes - so your date and filename need to be bytes.

@CyberDefend3r
Copy link
Author

That is my point. Your path to exiftool executable should NOT BE BYTES.

When you use the 'with' stmt - it ends up calling:

self._process = subprocess.Popen( [self.executable, "-stay_open", "True", "-@", "-", "-common_args", "-G", "-n"],

Where self.executable is what you called exifTool's constructor with. Popen takes strings.

However - when you call 'execute(...)' - it is writing to a pipe - and that required bytes - so your date and filename need to be bytes.

lol you're right i was being an idiot. I was thinking that was being passed to exiftool so I was doing :

self.exifTool = os.path.join(self.curDir.encode(), 'exiftool.exe'.encode())

AND for some reason:

self.exifTool = exiftool.fsencode(collection.exifTool)

So i changed both of those and ended up getting "OSError: [WinError 10038] An operation was attempted on something that is not a socket" I changed the exiftool.py to be from the commit recommended in that issue and all works now!

Thank you for the help! Much appreciated.

@CyberDefend3r
Copy link
Author

CyberDefend3r commented Nov 18, 2019

working code:

"""
Used to take pictures seperated into folders based on date like year/month/photo.jpg, and 
change the create date of photo to the year and month from the folder names.

The script assumes that exiftool.exe and script are in the same directory. year folders are 
in same dir as well.
"""

import os
from glob import glob
import exiftool

class collection:
    def __init__(self):
        self.dateFile = []
        self.fileList = {}
        self.paths = glob('**/**')
        self.curDir = os.getcwd()
        self.exifTool = os.path.join(self.curDir, 'exiftool.exe')
        self.overwriteOG = b'-overwrite_original'

    def setVariables(self):
        for path in self.paths:
            globPath = os.path.join(path, '*.*')
            picturPath = glob(globPath)
            pictures = []
            for pic in picturPath:
                pictures.append(os.path.join(self.curDir, pic))
            date = str.replace(path, '\\', ':') + ':01 00:01:01'
            self.fileList = {'date':date, 'pictures':pictures}
            self.dateFile.append(self.fileList)

class changeDate:
    def __init__(self, collection):
        self.dateFile = collection.dateFile
        self.exifTool = collection.exifTool
        self.overwriteOG = collection.overwriteOG

    def buildEXIFcommand(self):
        for filelist in self.dateFile:
            date = filelist['date']
            for picfile in filelist['pictures']:
                modAllDate = b'-AllDates=' + date.encode()
                modCreateDate = b'-FileCreateDate=' + date.encode()
                picfile = exiftool.fsencode(picfile)
                self.exifChange(modAllDate, modCreateDate, picfile)

    def exifChange(self, modAllDate, modCreateDate, picfile):
        with exiftool.ExifTool(self.exifTool) as et:
            et.execute(self.overwriteOG, modAllDate, modCreateDate, picfile)

if __name__ == "__main__":
    Collection = collection()
    print('Collecting Files To change')
    Collection.setVariables()
    ChangeDate = changeDate(Collection)
    print('Changing date on collected files')
    ChangeDate.buildEXIFcommand()
    print('Completed')

@amitabharora
Copy link

Old thread but this helped me understand how to pass arguments to execute. Can be confusing. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants