Skip to content

Commit cc542d2

Browse files
committed
remove print_conversion enhancement (from pull 25), combined the added_args and common_args into one thing, and also an enhancement on linux smarnach#11
1 parent 03a8595 commit cc542d2

File tree

2 files changed

+77
-27
lines changed

2 files changed

+77
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Date (Timezone) | Version | Comment
1818
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
1919
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**
2020
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.
21-
07/19/2019 12:01:22 AM (PTD) | 0.3.2 | fix the select() problem for windows, and fix all tests
21+
07/19/2019 12:01:22 AM (PDT) | 0.3.2 | fix the select() problem for windows, and fix all tests
22+
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
2223

2324

2425
# Changes around the web

exiftool.py

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@
6565
import logging
6666
import codecs
6767

68+
# for the pdeathsig
69+
import signal
70+
import ctypes
71+
6872
try: # Py3k compatibility
6973
basestring
7074
except NameError:
@@ -90,6 +94,9 @@
9094
KW_TAGNAME = "IPTC:Keywords"
9195
KW_REPLACE, KW_ADD, KW_REMOVE = range(3)
9296

97+
#------------------------------------------------------------------------------------------------
98+
99+
93100
# This code has been adapted from Lib/os.py in the Python source tree
94101
# (sha1 265e36e277f3)
95102
def _fscodec():
@@ -119,6 +126,27 @@ def fsencode(filename):
119126
fsencode = _fscodec()
120127
del _fscodec
121128

129+
#------------------------------------------------------------------------------------------------
130+
131+
def set_pdeathsig(sig=signal.SIGTERM):
132+
"""
133+
Use this method in subprocess.Popen(preexec_fn=set_pdeathsig()) to make sure,
134+
the exiftool childprocess is stopped if this process dies.
135+
However, this only works on linux.
136+
"""
137+
if sys.platform == "linux" or sys.platform == "linux2":
138+
def callable_method():
139+
# taken from linux/prctl.h
140+
pr_set_pdeathsig = 1
141+
libc = ctypes.CDLL("libc.so.6")
142+
return libc.prctl(pr_set_pdeathsig, sig)
143+
144+
return callable_method
145+
else:
146+
return None
147+
148+
149+
122150

123151
#string helper
124152
def strip_nl (s):
@@ -152,6 +180,10 @@ def format_error (result):
152180
else:
153181
return 'exiftool finished with error: "%s"' % strip_nl(result)
154182

183+
184+
185+
186+
#------------------------------------------------------------------------------------------------
155187
class ExifTool(object):
156188
"""Run the `exiftool` command-line tool and communicate to it.
157189
@@ -161,7 +193,7 @@ class ExifTool(object):
161193
name disables the print conversion for this particular tag.
162194
163195
You can pass two arguments to the constructor:
164-
- ``added_args`` (list of strings): contains additional paramaters for
196+
- ``common_args`` (list of strings): contains additional paramaters for
165197
the stay-open instance of exiftool
166198
- ``executable`` (string): file name of the ``exiftool`` executable.
167199
The default value ``exiftool`` will only work if the executable
@@ -196,57 +228,74 @@ class ExifTool(object):
196228
associated with a running subprocess.
197229
"""
198230

199-
def __init__(self, executable_=None, added_args=None, win_shell=True, print_conversion=False):
231+
def __init__(self, executable_=None, common_args=None, win_shell=True):
200232

201233
self.win_shell = win_shell
202-
self.print_conversion = print_conversion
203234

204235
if executable_ is None:
205236
self.executable = executable
206237
else:
207238
self.executable = executable_
208239
self.running = False
209-
210-
if added_args is None:
211-
self.added_args = []
212-
elif type(added_args) is list:
213-
self.added_args = added_args
240+
241+
self._common_args = common_args
242+
# it can't be none, check if it's a list, if not, error
243+
244+
self._process = None
245+
246+
if common_args is None:
247+
# default parameters to exiftool
248+
# -n = disable print conversion (speedup)
249+
self.common_args = ["-G", "-n"]
250+
elif type(common_args) is list:
251+
self.common_args = common_args
214252
else:
215-
raise TypeError("added_args not a list of strings")
253+
raise TypeError("common_args not a list of strings")
254+
216255

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

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

235-
proc_args.extend(self.added_args)
236274
logging.debug(proc_args)
237275

276+
238277
with open(os.devnull, "w") as devnull:
239-
startup_info = subprocess.STARTUPINFO()
240-
if not self.win_shell:
241-
SW_FORCEMINIMIZE = 11 # from win32con
242-
# Adding enum 11 (SW_FORCEMINIMIZE in win32api speak) will
243-
# keep it from throwing up a DOS shell when it launches.
244-
startup_info.dwFlags |= 11
245-
246-
self._process = subprocess.Popen(
247-
proc_args,
248-
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
249-
stderr=devnull, startupinfo=startup_info)
278+
if sys.platform == 'win32':
279+
startup_info = subprocess.STARTUPINFO()
280+
if not self.win_shell:
281+
SW_FORCEMINIMIZE = 11 # from win32con
282+
# Adding enum 11 (SW_FORCEMINIMIZE in win32api speak) will
283+
# keep it from throwing up a DOS shell when it launches.
284+
startup_info.dwFlags |= 11
285+
286+
self._process = subprocess.Popen(
287+
proc_args,
288+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
289+
stderr=devnull, startupinfo=startup_info)
290+
# TODO check error before saying it's running
291+
else:
292+
# assume it's linux
293+
self._process = subprocess.Popen(
294+
proc_args,
295+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
296+
stderr=devnull, preexec_fn=set_pdeathsig(signal.SIGTERM))
297+
# TODO check error before saying it's running
298+
250299
self.running = True
251300

252301
def terminate(self):

0 commit comments

Comments
 (0)