Skip to content
This repository has been archived by the owner on Dec 30, 2018. It is now read-only.

Commit

Permalink
Abstract: Never have more than 1 handle opened. With some Sane backen…
Browse files Browse the repository at this point in the history
…ds, it ends bad (I/O errors)

Signed-off-by: Jerome Flesch <jflesch@gmail.com>
  • Loading branch information
jflesch committed May 28, 2012
1 parent ae2a804 commit 7c86da5
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 38 deletions.
67 changes: 39 additions & 28 deletions src/abstract.py
Expand Up @@ -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()


Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand All @@ -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):
Expand All @@ -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()
Expand All @@ -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):
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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)
Expand Down
24 changes: 14 additions & 10 deletions tests/tests_abstract.py
Expand Up @@ -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")
Expand All @@ -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):
Expand Down

0 comments on commit 7c86da5

Please sign in to comment.