Skip to content

Commit

Permalink
remove print_conversion enhancement (from pull 25), combined the adde…
Browse files Browse the repository at this point in the history
…d_args and common_args into one thing, and also an enhancement on linux smarnach#11
  • Loading branch information
sylikc committed Jul 19, 2019
1 parent 03a8595 commit cc542d2
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 27 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Date (Timezone) | Version | Comment
07/18/2019 04:14:32 AM (PDT) | 0.1.9 | Merge the test cases from the [Pull request #5 "add set_tags_batch, set_tags + constructor takes added options"](https://github.com/smarnach/pyexiftool/pull/5) by [halloleo](https://github.com/halloleo) on Aug 1, 2012
07/18/2019 04:34:46 AM (PDT) | 0.3.0 | changed the setup.py licensing and updated the version numbering as in changelog<br>changed the version number scheme, as it appears the "official last release" was 0.2.0 tagged. There's going to be a lot of things broken in this current build, and I'll fix it as they come up. I'm going to start playing with the library and the included tests and such. <br>There's one more pull request #11 which would be pending, but it duplicates the extra arguments option. <br>I'm also likely to remove the print conversion as it's now covered by the extra args. I'll also rename some variable names with the addedargs patch<br>**for my changes (sylikc), I can only guarantee they will work on Python 3.7, because that's my environment... and while I'll try to maintain compatibility, there's no guarantees**
07/18/2019 05:06:19 AM (PDT) | 0.3.1 | make some minor tweaks to the naming of the extra args variable. The other pull request 11 names them params, and when I decide how to merge that pull request, I'll probably change the variable names again.
07/19/2019 12:01:22 AM (PTD) | 0.3.2 | fix the select() problem for windows, and fix all tests
07/19/2019 12:01:22 AM (PDT) | 0.3.2 | fix the select() problem for windows, and fix all tests
07/19/2019 12:54:39 AM (PDT) | 0.3.3 | Merge a piece of [Pull request #11 "Robustness enhancements](https://github.com/smarnach/pyexiftool/pull/11) by [Matthias Kiefer (kiefermat)](https://github.com/kiefermat) on Oct 27, 2014<br>*On linux call prctl in subprocess to be sure that the exiftool child process is killed even if the parent process is killed by itself*<br>also removed print_conversion<br>also merged the common_args and added_args into one args list


# Changes around the web
Expand Down
101 changes: 75 additions & 26 deletions exiftool.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
import logging
import codecs

# for the pdeathsig
import signal
import ctypes

try: # Py3k compatibility
basestring
except NameError:
Expand All @@ -90,6 +94,9 @@
KW_TAGNAME = "IPTC:Keywords"
KW_REPLACE, KW_ADD, KW_REMOVE = range(3)

#------------------------------------------------------------------------------------------------


# This code has been adapted from Lib/os.py in the Python source tree
# (sha1 265e36e277f3)
def _fscodec():
Expand Down Expand Up @@ -119,6 +126,27 @@ def fsencode(filename):
fsencode = _fscodec()
del _fscodec

#------------------------------------------------------------------------------------------------

def set_pdeathsig(sig=signal.SIGTERM):
"""
Use this method in subprocess.Popen(preexec_fn=set_pdeathsig()) to make sure,
the exiftool childprocess is stopped if this process dies.
However, this only works on linux.
"""
if sys.platform == "linux" or sys.platform == "linux2":
def callable_method():
# taken from linux/prctl.h
pr_set_pdeathsig = 1
libc = ctypes.CDLL("libc.so.6")
return libc.prctl(pr_set_pdeathsig, sig)

return callable_method
else:
return None




#string helper
def strip_nl (s):
Expand Down Expand Up @@ -152,6 +180,10 @@ def format_error (result):
else:
return 'exiftool finished with error: "%s"' % strip_nl(result)




#------------------------------------------------------------------------------------------------
class ExifTool(object):
"""Run the `exiftool` command-line tool and communicate to it.
Expand All @@ -161,7 +193,7 @@ class ExifTool(object):
name disables the print conversion for this particular tag.
You can pass two arguments to the constructor:
- ``added_args`` (list of strings): contains additional paramaters for
- ``common_args`` (list of strings): contains additional paramaters for
the stay-open instance of exiftool
- ``executable`` (string): file name of the ``exiftool`` executable.
The default value ``exiftool`` will only work if the executable
Expand Down Expand Up @@ -196,57 +228,74 @@ class ExifTool(object):
associated with a running subprocess.
"""

def __init__(self, executable_=None, added_args=None, win_shell=True, print_conversion=False):
def __init__(self, executable_=None, common_args=None, win_shell=True):

self.win_shell = win_shell
self.print_conversion = print_conversion

if executable_ is None:
self.executable = executable
else:
self.executable = executable_
self.running = False

if added_args is None:
self.added_args = []
elif type(added_args) is list:
self.added_args = added_args

self._common_args = common_args
# it can't be none, check if it's a list, if not, error

self._process = None

if common_args is None:
# default parameters to exiftool
# -n = disable print conversion (speedup)
self.common_args = ["-G", "-n"]
elif type(common_args) is list:
self.common_args = common_args
else:
raise TypeError("added_args not a list of strings")
raise TypeError("common_args not a list of strings")


def start(self):
"""Start an ``exiftool`` process in batch mode for this instance.
This method will issue a ``UserWarning`` if the subprocess is
already running. The process is started with the ``-G`` (and,
already running. The process is by default started with the ``-G`` (and,
if print conversion was disabled, ``-n``) as common arguments,
which are automatically included in every command you run with
:py:meth:`execute()`.
However, you can override these default arguments with the common_args parameter in the constructor.
"""
if self.running:
warnings.warn("ExifTool already running; doing nothing.")
return

proc_args = [self.executable, "-stay_open", "True", "-@", "-", "-common_args", "-G"]
# may remove this and just have it added to extra args
if not self.print_conversion:
proc_args.append("-n")
proc_args = [self.executable, "-stay_open", "True", "-@", "-", "-common_args"]
proc_args.extend(self.common_args) # add the common arguments

proc_args.extend(self.added_args)
logging.debug(proc_args)


with open(os.devnull, "w") as devnull:
startup_info = subprocess.STARTUPINFO()
if not self.win_shell:
SW_FORCEMINIMIZE = 11 # from win32con
# Adding enum 11 (SW_FORCEMINIMIZE in win32api speak) will
# keep it from throwing up a DOS shell when it launches.
startup_info.dwFlags |= 11

self._process = subprocess.Popen(
proc_args,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=devnull, startupinfo=startup_info)
if sys.platform == 'win32':
startup_info = subprocess.STARTUPINFO()
if not self.win_shell:
SW_FORCEMINIMIZE = 11 # from win32con
# Adding enum 11 (SW_FORCEMINIMIZE in win32api speak) will
# keep it from throwing up a DOS shell when it launches.
startup_info.dwFlags |= 11

self._process = subprocess.Popen(
proc_args,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=devnull, startupinfo=startup_info)
# TODO check error before saying it's running
else:
# assume it's linux
self._process = subprocess.Popen(
proc_args,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=devnull, preexec_fn=set_pdeathsig(signal.SIGTERM))
# TODO check error before saying it's running

self.running = True

def terminate(self):
Expand Down

0 comments on commit cc542d2

Please sign in to comment.