Remove _errors and ghostscript test

pull/2/head
Vinayak Mehta 2018-12-18 07:43:52 +05:30
parent 9879a87c6f
commit 4938c48853
5 changed files with 48 additions and 257 deletions

View File

@ -4,6 +4,7 @@
ghostscript - A Python interface for the Ghostscript interpreter C-API
"""
#
# Modifications 2018 by Vinayak Mehta <vmehta94@gmail.com>
# Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>
#
# This program is free software: you can redistribute it and/or modify
@ -20,60 +21,22 @@ ghostscript - A Python interface for the Ghostscript interpreter C-API
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>"
__licence__ = "GNU General Public License version 3 (GPL v3)"
__version__ = '0.6'
__all__ = ['Ghostscript', 'revision',
'GhostscriptError', 'PleaseDisplayUsage']
import atexit
from . import _gsprint as gs
GhostscriptError = gs.GhostscriptError
__author__ = 'Hartmut Goebel <h.goebel@crazy-compilers.com>'
__copyright__ = 'Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>'
__license__ = 'GNU General Public License version 3 (GPL v3)'
__version__ = '0.6'
def PleaseDisplayUsage(Warning):
"""
This exception is raised when Ghostscript asks the application to
display the usage. The application should catch the exception an
print the usage message.
"""
pass
def revision():
"""
This function returns the revision numbers and strings of the
Ghostscript interpreter library as a dict. You should call it
before any other interpreter library functions to make sure that
the correct version of the Ghostscript interpreter has been
loaded.
"""
rev = gs.revision()
return dict((f, getattr(rev, f)) for f, _ in rev._fields_)
MAX_STRING_LENGTH = gs.MAX_STRING_LENGTH
class Ghostscript(object):
@staticmethod
def revision():
return revision()
class __Ghostscript(object):
def __init__(self, instance, args, stdin=None, stdout=None, stderr=None):
self._initialized = False
self._callbacks = None
if stdin or stdout or stderr:
self.set_stdio(stdin, stdout, stderr)
rc = gs.init_with_args(instance, args)
if rc == gs.e_Info:
raise PleaseDisplayUsage
self._initialized = True
if rc == gs.e_Quit:
self.exit()
@ -117,15 +80,10 @@ class Ghostscript(object):
self._initialized = False
__Ghostscript = Ghostscript
__instance__ = None
def Ghostscript(*args, **kwargs):
"""Factory function for setting up a Ghostscript instance
"""
Factory function for setting up a Ghostscript instance
"""
global __instance__, __object_count__
global __instance__
# Ghostscript only supports a single instance
if __instance__ is None:
__instance__ = gs.new_instance()
@ -133,3 +91,6 @@ def Ghostscript(*args, **kwargs):
stdin=kwargs.get('stdin', None),
stdout=kwargs.get('stdout', None),
stderr=kwargs.get('stderr', None))
__instance__ = None

View File

@ -1,123 +0,0 @@
"""
Definition of Ghostscript error codes
"""
#
# Copyright (C) 2010-2018 by Hartmut Goebel
#
# Based on iapi.h which is
# Copyright (C) 1989, 1995, 1998, 1999 Aladdin Enterprises. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>"
__licence__ = "GNU General Public License version 3 (GPL v3)"
__version__ = "0.6"
LEVEL1_ERROR_NAMES = ["unknownerror", "dictfull", "dictstackoverflow",
"dictstackunderflow", "execstackoverflow",
"interrupt", "invalidaccess", "invalidexit",
"invalidfileaccess", "invalidfont",
"invalidrestore", "ioerror", "limitcheck",
"nocurrentpoint", "rangecheck", "stackoverflow",
"stackunderflow", "syntaxerror", "timeout",
"typecheck", "undefined", "undefinedfilename",
"undefinedresult", "unmatchedmark", "VMerror"]
LEVEL2_ERROR_NAMES = ["configurationerror", "invalidcontext",
"undefinedresource", "unregistered",
"invalidid"]
ERROR_NAMES = LEVEL1_ERROR_NAMES + LEVEL2_ERROR_NAMES
PSEUDO_ERROR_NAMES = ['Fatal', 'Quit', 'InterpreterExit', 'RemapColor',
'ExecStackUnderflow', 'VMreclaim', 'NeedInput',
'NeedStdin', 'NeedStdout', 'NeedStderr', 'Info']
def error2name(ecode):
if ecode <= e_Fatal:
return PSEUDO_ERROR_NAMES[-ecode-100]
else:
return ERROR_NAMES[-ecode-1]
#
# Internal code for a fatal error.
# gs_interpret also returns this for a .quit with a positive exit code.
#
e_Fatal = -100
#
# Internal code for the .quit operator.
# The real quit code is an integer on the operand stack.
# gs_interpret returns this only for a .quit with a zero exit code.
#
e_Quit = -101
#
# Internal code for a normal exit from the interpreter.
# Do not use outside of interp.c.
#
e_InterpreterExit = -102
#
# Internal code that indicates that a procedure has been stored in the
# remap_proc of the graphics state, and should be called before retrying
# the current token. This is used for color remapping involving a call
# back into the interpreter -- inelegant, but effective.
#
e_RemapColor = -103
#
# Internal code to indicate we have underflowed the top block
# of the e-stack.
#
e_ExecStackUnderflow = -104
#
# Internal code for the vmreclaim operator with a positive operand.
# We need to handle this as an error because otherwise the interpreter
# won't reload enough of its state when the operator returns.
#
e_VMreclaim = -105
#
# Internal code for requesting more input from run_string.
#
e_NeedInput = -106
#
# Internal code for stdin callout.
#
e_NeedStdin = -107
#
# Internal code for stdout callout.
#
e_NeedStdout = -108
#
# Internal code for stderr callout.
#
e_NeedStderr = -109
#
# Internal code for a normal exit when usage info is displayed.
# This allows Window versions of Ghostscript to pause until
# the message can be read.
#
e_Info = -110

View File

@ -4,6 +4,7 @@
ghostscript._gsprint - A low-level interface to the Ghostscript C-API using ctypes
"""
#
# Modifications 2018 by Vinayak Mehta <vmehta94@gmail.com>
# Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>
#
# Display_callback Structure by Lasse Fister <commander@graphicore.de> in 2013
@ -22,62 +23,38 @@ ghostscript._gsprint - A low-level interface to the Ghostscript C-API using ctyp
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>"
__licence__ = "GNU General Public License version 3 (GPL v3)"
__version__ = "0.6"
import sys
from ctypes import *
from ._errors import e_Quit, e_Info
# base/gserrors.h
#
# Internal code for a normal exit when usage info is displayed.
# This allows Window versions of Ghostscript to pause until
# the message can be read.
#
e_Info = -110
MAX_STRING_LENGTH = 65535
#
# Internal code for the .quit operator.
# The real quit code is an integer on the operand stack.
# gs_interpret returns this only for a .quit with a zero exit code.
#
e_Quit = -101
__author__ = 'Hartmut Goebel <h.goebel@crazy-compilers.com>'
__copyright__ = 'Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>'
__license__ = 'GNU General Public License version 3 (GPL v3)'
__version__ = '0.6'
gs_main_instance = c_void_p
display_callback = c_void_p
class Revision(Structure):
_fields_ = [
("product", c_char_p),
("copyright", c_char_p),
("revision", c_long),
("revisiondate", c_long)
]
# https://www.ghostscript.com/doc/current/API.htm
class GhostscriptError(Exception):
def __init__(self, ecode):
# :todo:
Exception.__init__(self, error2name(ecode))
self.code = ecode
def revision():
"""
Get version numbers and strings.
This is safe to call at any time.
You should call this first to make sure that the correct version
of the Ghostscript is being used.
Returns a Revision instance
"""
revision = Revision()
rc = libgs.gsapi_revision(pointer(revision), sizeof(revision))
if rc:
raise ArgumentError("Revision structure size is incorrect, "
"requires %s bytes" % rc)
return revision
def new_instance(): # display_callback=None):
"""
Create a new instance of Ghostscript
def new_instance():
"""Create a new instance of Ghostscript
This instance is passed to most other API functions.
"""
@ -91,8 +68,7 @@ def new_instance(): # display_callback=None):
def delete_instance(instance):
"""
Destroy an instance of Ghostscript
"""Destroy an instance of Ghostscript
Before you call this, Ghostscript must have finished.
If Ghostscript has been initialised, you must call exit()
@ -105,11 +81,9 @@ c_stdstream_call_t = CFUNCTYPE(c_int, gs_main_instance, POINTER(c_char), c_int)
def _wrap_stdin(infp):
"""
Wrap a filehandle into a C function to be used as `stdin` callback
"""Wrap a filehandle into a C function to be used as `stdin` callback
for ``set_stdio``. The filehandle has to support the readline() method.
"""
def _wrap(instance, dest, count):
try:
data = infp.readline(count)
@ -127,12 +101,10 @@ def _wrap_stdin(infp):
def _wrap_stdout(outfp):
"""
Wrap a filehandle into a C function to be used as `stdout` or
"""Wrap a filehandle into a C function to be used as `stdout` or
`stderr` callback for ``set_stdio``. The filehandle has to support the
write() and flush() methods.
"""
def _wrap(instance, str, count):
outfp.write(str[:count])
outfp.flush()
@ -145,8 +117,7 @@ _wrap_stderr = _wrap_stdout
def set_stdio(instance, stdin, stdout, stderr):
"""
Set the callback functions for stdio.
"""Set the callback functions for stdio.
``stdin``, ``stdout`` and ``stderr`` have to be ``ctypes``
callback functions matching the ``_gsprint.c_stdstream_call_t``
@ -173,13 +144,8 @@ def set_stdio(instance, stdin, stdout, stderr):
return rc
# :todo: set_poll (instance, int(*poll_fn)(void *caller_handle));
# :todo: set_display_callback(instance, callback):
def init_with_args(instance, argv):
"""
Initialise the interpreter.
"""Initialise the interpreter
1. If quit or EOF occur during init_with_args(), the return value
will be e_Quit. This is not an error. You must call exit() and
@ -200,8 +166,7 @@ def init_with_args(instance, argv):
def exit(instance):
"""
Exit the interpreter
"""Exit the interpreter
This must be called on shutdown if init_with_args() has been
called, and just before delete_instance()
@ -230,7 +195,7 @@ def __win32_finddll():
# Look up different variants of Ghostscript and take the highest
# version for which the DLL is to be found in the filesystem.
for key_name in ('AFPL Ghostscript', 'Aladdin Ghostscript',
'GPL Ghostscript', 'GNU Ghostscript'):
'GNU Ghostscript', 'GPL Ghostscript'):
try:
k1 = OpenKey(HKEY_LOCAL_MACHINE, "Software\\%s" % key_name)
for num in range(0, QueryInfoKey(k1)[0]):
@ -256,17 +221,18 @@ def __win32_finddll():
if sys.platform == 'win32':
libgs = __win32_finddll()
if not libgs:
raise RuntimeError('Can not find Ghostscript DLL in registry')
raise RuntimeError('Please make sure that Ghostscript is installed')
libgs = windll.LoadLibrary(libgs)
else:
try:
libgs = cdll.LoadLibrary("libgs.so")
libgs = cdll.LoadLibrary('libgs.so')
except OSError:
# shared object file not found
import ctypes.util
libgs = ctypes.util.find_library('gs')
if not libgs:
raise RuntimeError('Can not find Ghostscript library (libgs)')
raise RuntimeError('Please make sure that Ghostscript is installed')
libgs = cdll.LoadLibrary(libgs)
del __win32_finddll

View File

@ -181,8 +181,10 @@ class Lattice(BaseParser):
gs_call = '-q -sDEVICE=png16m -o {} -r600 {}'.format(
self.imagename, self.filename)
gs_call = gs_call.encode().split()
with Ghostscript(*gs_call) as gs:
null = open(os.devnull, 'wb')
with Ghostscript(*gs_call, stdout=null) as gs:
pass
null.close()
def _generate_table_bbox(self):
self.image, self.threshold = adaptive_threshold(

View File

@ -74,21 +74,6 @@ def test_no_tables_found_warnings_suppressed():
pytest.fail('Unexpected warning: {}'.format(warning_text))
def test_ghostscript_not_found(monkeypatch):
import distutils
def _find_executable_patch(arg):
return ''
monkeypatch.setattr(distutils.spawn, 'find_executable', _find_executable_patch)
message = ('Please make sure that Ghostscript is installed and available'
' on the PATH environment variable')
filename = os.path.join(testdir, 'foo.pdf')
with pytest.raises(Exception, message=message):
tables = camelot.read_pdf(filename)
def test_no_password():
filename = os.path.join(testdir, 'health_protected.pdf')
message = 'file has not been decrypted'