Skip to content
Browse files

Abstract: Never have more than 1 handle opened. With some Sane backen…

…ds, it ends bad (I/O errors)

Signed-off-by: Jerome Flesch <jflesch@gmail.com>
  • Loading branch information...
1 parent ae2a804 commit 7c86da5fd471f17481df99ba91593fcf7b81570f @jflesch committed May 28, 2012
Showing with 53 additions and 38 deletions.
  1. +39 −28 src/abstract.py
  2. +14 −10 tests/tests_abstract.py
View
67 src/abstract.py
@@ -8,19 +8,25 @@
'get_devices',
]
-_sane_is_init = 0
+sane_is_init = 0
+
+# XXX(Jflesch): Never open more than one handle at the same time.
+# Some Sane backends don't support it. For instance, I have 2 HP scanners, and
+# if I try to access both from the same process, I get I/O errors.
+sane_dev_handle = ("", None)
+
def sane_init():
- global _sane_is_init
- if _sane_is_init <= 0:
+ global sane_is_init
+ if sane_is_init <= 0:
rawapi.sane_init()
- _sane_is_init += 1
+ sane_is_init += 1
def sane_exit():
- global _sane_is_init
- _sane_is_init -= 1
- if _sane_is_init <= 0:
+ global sane_is_init
+ sane_is_init -= 1
+ if sane_is_init <= 0:
rawapi.sane_exit()
@@ -58,11 +64,11 @@ def build_from_rawapi(scanner, opt_idx, opt_raw):
def __get_value(self):
self.__scanner._open()
- return rawapi.sane_get_option_value(self.__scanner._handle, self.idx)
+ return rawapi.sane_get_option_value(sane_dev_handle[1], self.idx)
def __set_value(self, new_value):
self.__scanner._open()
- rawapi.sane_set_option_value(self.__scanner._handle, self.idx, new_value)
+ rawapi.sane_set_option_value(sane_dev_handle[1], self.idx, new_value)
value = property(__get_value, __set_value)
@@ -130,19 +136,19 @@ def __init__(self, scanner):
self.__img = None
self.__scanner._open()
- rawapi.sane_start(self.__scanner._handle)
+ rawapi.sane_start(sane_dev_handle[1])
try:
self.__parameters = \
- rawapi.sane_get_parameters(self.__scanner._handle)
+ rawapi.sane_get_parameters(sane_dev_handle[1])
except Exception, exc:
- rawapi.sane_cancel(self.__scanner._handle)
+ rawapi.sane_cancel(sane_dev_handle[1])
raise exc
def read(self):
try:
- self.__raw_output += rawapi.sane_read(self.__scanner._handle)
+ self.__raw_output += rawapi.sane_read(sane_dev_handle[1])
except EOFError, exc:
- rawapi.sane_cancel(self.__scanner._handle)
+ rawapi.sane_cancel(sane_dev_handle[1])
self.__is_scanning = False
self.__img = ImgUtil.raw_to_img(self.__raw_output,
self.__parameters)
@@ -166,7 +172,7 @@ def get_img(self, number=0):
def __del__(self):
if self.__is_scanning:
- rawapi.sane_cancel(self.__scanner._handle)
+ rawapi.sane_cancel(sane_dev_handle[1])
class MultiScanSession(object):
@@ -184,20 +190,20 @@ def __init__(self, scanner):
def read(self):
try:
if not self.__is_scanning:
- rawapi.sane_start(self.__scanner._handle)
+ rawapi.sane_start(sane_dev_handle[1])
self.__is_scanning = True
self.__must_clean = True
self.__parameters = \
- rawapi.sane_get_parameters(self.__scanner._handle)
+ rawapi.sane_get_parameters(sane_dev_handle[1])
return
try:
- self.__raw_output += rawapi.sane_read(self.__scanner._handle)
+ self.__raw_output += rawapi.sane_read(sane_dev_handle[1])
except EOFError, exc:
self.__imgs.append(ImgUtil.raw_to_img(self.__raw_output,
self.__parameters))
self.__is_scanning = False
except StopIteration, exc:
- rawapi.sane_cancel(self.__scanner._handle)
+ rawapi.sane_cancel(sane_dev_handle[1])
self.__must_clean = False
self.__is_scanning = False
raise EOFError()
@@ -216,7 +222,7 @@ def get_img(self, number):
def __del__(self):
if self.__must_clean:
- rawapi.sane_cancel(self.__scanner._handle)
+ rawapi.sane_cancel(sane_dev_handle[1])
class Scanner(object):
@@ -226,7 +232,6 @@ def __init__(self, name, vendor="Unknown", model="Unknown",
self.vendor = vendor
self.model = model
self.dev_type = dev_type
- self._handle = None
self.__options = None # { "name" : ScannerOption }
@staticmethod
@@ -235,17 +240,23 @@ def build_from_rawapi(sane_device):
sane_device.type)
def _open(self):
- if self._handle != None:
+ global sane_dev_handle
+ (devid, handle) = sane_dev_handle
+ if devid == self.name:
return
+ self.force_close()
sane_init()
- self._handle = rawapi.sane_open(self.name)
+ handle = rawapi.sane_open(self.name)
+ sane_dev_handle = (self.name, handle)
def force_close(self):
- if self._handle == None:
+ global sane_dev_handle
+ (devid, handle) = sane_dev_handle
+ if handle == None:
return
- rawapi.sane_close(self._handle)
- self._handle = None
+ rawapi.sane_close(handle)
sane_exit()
+ sane_dev_handle = ("", None)
def __del__(self):
self.force_close()
@@ -254,10 +265,10 @@ def __load_options(self):
if self.__options != None:
return
self._open()
- nb_options = rawapi.sane_get_option_value(self._handle, 0)
+ nb_options = rawapi.sane_get_option_value(sane_dev_handle[1], 0)
self.__options = {}
for opt_idx in range(1, nb_options):
- opt_desc = rawapi.sane_get_option_descriptor(self._handle, opt_idx)
+ opt_desc = rawapi.sane_get_option_descriptor(sane_dev_handle[1], opt_idx)
if not rawapi.SaneValueType(opt_desc.type).can_getset_opt():
continue
opt = ScannerOption.build_from_rawapi(self, opt_idx, opt_desc)
View
24 tests/tests_abstract.py
@@ -21,21 +21,23 @@ def tearDown(self):
class TestSaneOptions(unittest.TestCase):
def setUp(self):
- devices = abstract.get_devices()
- self.assertTrue(len(devices) > 0)
- self.dev = devices[0]
+ self.devices = abstract.get_devices()
+ self.assertTrue(len(self.devices) > 0)
def test_get_option(self):
- val = self.dev.options['mode'].value
- self.assertNotEqual(val, None)
+ for dev in self.devices:
+ val = dev.options['mode'].value
+ self.assertNotEqual(val, None)
def test_set_option(self):
- self.dev.options['mode'].value = "Gray"
- val = self.dev.options['mode'].value
- self.assertEqual(val, "Gray")
+ for dev in self.devices:
+ dev.options['mode'].value = "Gray"
+ val = dev.options['mode'].value
+ self.assertEqual(val, "Gray")
def __set_opt(self, opt_name, opt_val):
- self.dev.options[opt_name].value = opt_val
+ for dev in self.devices:
+ dev.options[opt_name].value = opt_val
def test_set_inexisting_option(self):
self.assertRaises(KeyError, self.__set_opt, 'xyz', "Gray")
@@ -44,7 +46,9 @@ def test_set_invalid_value(self):
self.assertRaises(rawapi.SaneException, self.__set_opt, 'mode', "XYZ")
def tearDown(self):
- del(self.dev)
+ for dev in self.devices:
+ del(dev)
+ del(self.devices)
class TestSaneScan(unittest.TestCase):

0 comments on commit 7c86da5

Please sign in to comment.
Something went wrong with that request. Please try again.