source: server/lib/gutenbach/server/printer.py @ d21198f

no-cups
Last change on this file since d21198f was d21198f, checked in by Jessica B. Hamrick <jhamrick@…>, 12 years ago

Fix some threading deadlock bugs

  • Property mode set to 100644
File size: 9.2 KB
RevLine 
[eee389a]1from . import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
[e58af05]2from . import Job
[b01b6d1]3from gutenbach.ipp import PrinterStates as States
[b2e077a]4import gutenbach.ipp as ipp
5import logging
6import time
[eee389a]7import threading
8import heapq
9import traceback
10import sys
11
[d04a689]12
13# initialize logger
14logger = logging.getLogger(__name__)
[776a659]15
[eee389a]16class GutenbachPrinter(threading.Thread):
[b2e077a]17
[1a63bf7]18    # for IPP
[b2e077a]19    attributes = [
20        "printer-uri-supported",
21        "uri-authentication-supported",
22        "uri-security-supported",
23        "printer-name",
24        "printer-state",
25        "printer-state-reasons",
26        "ipp-versions-supported",
27        "operations-supported",
28        "charset-configured",
29        "charset-supported",
30        "natural-language-configured",
31        "generated-natural-language-supported",
32        "document-format-default",
33        "document-format-supported",
34        "printer-is-accepting-jobs",
35        "queued-job-count",
36        "pdl-override-supported",
37        "printer-up-time",
[f6e2532]38        "compression-supported",
39        "multiple-operation-time-out",
40        "multiple-document-jobs-supported",
[1a63bf7]41    ]
[b2e077a]42
[f6e2532]43    operations = [
44        "print-job",
45        "complete-job",
46        "start-job",
47        "get-job",
48        "get-jobs",
49    ]
50       
[eee389a]51    def __init__(self, name, *args, **kwargs):
52
[d21198f]53        super(GutenbachPrinter, self).__init__(*args, **kwargs)
54       
55        self.name = name
56        self.time_created = int(time.time())
[b2e077a]57
[d21198f]58        self.finished_jobs = []
59        self.pending_jobs = []
60        self.current_job = None
61        self.jobs = {}
[b2e077a]62
[d21198f]63        self.lock = threading.RLock()
64        self.running = False
65        self.paused = False
[776a659]66
[d21198f]67        # CUPS ignores jobs with id 0, so we have to start at 1
68        self._next_job_id = 1
[b01b6d1]69
70    def __repr__(self):
71        return str(self)
72
73    def __str__(self):
74        return "<Printer '%s'>" % self.name
75
76    ######################################################################
77    ###                          Properties                            ###
78    ######################################################################
79
80    @property
81    def uris(self):
82        uris = ["ipp://localhost:8000/printers/" + self.name,
83                "ipp://localhost/printers/" + self.name]
84        return uris
85   
86    @property
87    def uri(self):
88        return self.uris[0]
89
90    @property
[eee389a]91    def state(self):
92        with self.lock:
93            if self.current_job is not None:
94                val = States.PROCESSING
95            elif len(self.pending_jobs) == 0:
96                val = States.IDLE
97            else:
98                val = States.STOPPED
99        return val
100
101    @property
102    def active_jobs(self):
103        with self.lock:
104            jobs = self.pending_jobs[:]
105            if self.current_job is not None:
106                jobs.insert(0, self.current_job.id)
107        return jobs
[b01b6d1]108
109    ######################################################################
110    ###                            Methods                             ###
111    ######################################################################
112
[eee389a]113    def run(self):
114        self.running = True
115        while self.running:
116            with self.lock:
117                try:
118                    if self.current_job is None:
119                        self.start_job()
120                    elif self.current_job.is_finished:
121                        self.complete_job()
122                except:
123                    logger.fatal(traceback.format_exc())
124                    sys.exit(1)
125            time.sleep(0.1)
126
127    def start_job(self):
128        with self.lock:
129            if self.current_job is None:
130                try:
131                    job_id = heapq.heappop(self.pending_jobs)
132                    self.current_job = self.get_job(job_id)
[d21198f]133                    print "before play"
[eee389a]134                    self.current_job.play()
[d21198f]135                    print "after play"
[eee389a]136                except IndexError:
[d21198f]137                    self.current_job = None
[eee389a]138                except InvalidJobStateException:
139                    heapq.heappush(self.pending_jobs, self.current_job.id)
140                    self.current_job = None
141                   
142    def complete_job(self):
143        with self.lock:
144            if self.current_job is None:
145                return
146
147            try:
148                if not self.current_job.is_finished:
149                    self.current_job.stop()
150            finally:
151                self.finished_jobs.append(self.current_job)
152                self.current_job = None
[1a63bf7]153
[b01b6d1]154    def stop(self):
[eee389a]155        pass
156
157    def get_job(self, job_id):
158        with self.lock:
159            if job_id not in self.jobs:
160                raise InvalidJobException(job_id)
161            job = self.jobs[job_id]
162        return job
[b01b6d1]163
164    ######################################################################
165    ###                        IPP Attributes                          ###
166    ######################################################################
[1a63bf7]167
[b2e077a]168    @property
169    def printer_uri_supported(self):
[793432f]170        return ipp.PrinterUriSupported(self.uri)
[1a63bf7]171
[b2e077a]172    @property
173    def uri_authentication_supported(self):
[793432f]174        return ipp.UriAuthenticationSupported("none")
[b2e077a]175
176    @property
177    def uri_security_supported(self):
[793432f]178        return ipp.UriSecuritySupported("none")
[b2e077a]179
180    @property
181    def printer_name(self):
[793432f]182        return ipp.PrinterName(self.name)
[1a63bf7]183
[b2e077a]184    @property
185    def printer_state(self):
[b01b6d1]186        return ipp.PrinterState(self.state)
[1a63bf7]187
[b2e077a]188    @property
189    def printer_state_reasons(self):
[793432f]190        return ipp.PrinterStateReasons("none")
[b2e077a]191
192    @property
193    def ipp_versions_supported(self):
[793432f]194        return ipp.IppVersionsSupported("1.0", "1.1")
[1a63bf7]195
[f6e2532]196    # XXX: We should query ourself for the supported operations
[b2e077a]197    @property
198    def operations_supported(self):
[793432f]199        return ipp.OperationsSupported(ipp.OperationCodes.GET_JOBS)
[b2e077a]200
201    @property
202    def charset_configured(self):
[793432f]203        return ipp.CharsetConfigured("utf-8")
[b2e077a]204
205    @property
206    def charset_supported(self):
[793432f]207        return ipp.CharsetSupported("utf-8")
[b2e077a]208
209    @property
210    def natural_language_configured(self):
[793432f]211        return ipp.NaturalLanguageConfigured("en-us")
[b2e077a]212
213    @property
214    def generated_natural_language_supported(self):
[793432f]215        return ipp.GeneratedNaturalLanguageSupported("en-us")
[b2e077a]216
217    @property
218    def document_format_default(self):
[793432f]219        return ipp.DocumentFormatDefault("application/octet-stream")
[b2e077a]220
221    @property
222    def document_format_supported(self):
[793432f]223        return ipp.DocumentFormatSupported("application/octet-stream", "audio/mp3")
[b2e077a]224
225    @property
226    def printer_is_accepting_jobs(self):
[793432f]227        return ipp.PrinterIsAcceptingJobs(True)
[b2e077a]228
229    @property
230    def queued_job_count(self):
[793432f]231        return ipp.QueuedJobCount(len(self.active_jobs))
[b2e077a]232
233    @property
234    def pdl_override_supported(self):
[793432f]235        return ipp.PdlOverrideSupported("not-attempted")
[b2e077a]236
237    @property
238    def printer_up_time(self):
[793432f]239        return ipp.PrinterUpTime(int(time.time()) - self.time_created)
[b2e077a]240
241    @property
242    def compression_supported(self):
[793432f]243        return ipp.CompressionSupported("none")
[b2e077a]244
[f6e2532]245    @property
246    def multiple_operation_time_out(self):
[793432f]247        return ipp.MultipleOperationTimeOut(240)
[f6e2532]248
249    @property
250    def multiple_document_jobs_supported(self):
[793432f]251        return ipp.MultipleDocumentJobsSupported(False)
[f6e2532]252
[ee8e6d0]253
[b01b6d1]254    ######################################################################
255    ###                        IPP Operations                          ###
256    ######################################################################
257
258    def print_job(self):
259        pass
260
261    def validate_job(self):
262        pass
263
264    def get_jobs(self, requesting_user_name="", which_jobs=None):
265        # Filter by the which-jobs attribute
266        if which_jobs is None:
267            jobs = self.jobs.values()
268        elif which_jobs == "completed":
269            jobs = [self.jobs[job_id] for job_id in self.finished_jobs]
270        elif which_jobs == "not-completed":
271            jobs = [self.jobs[job_id] for job_id in self.active_jobs]
[ee8e6d0]272        else:
[b01b6d1]273            raise ipp.errors.ClientErrorAttributes(
274                which_jobs, ipp.WhichJobs(which_jobs))
[b2e077a]275
[b01b6d1]276        # Filter by username
277        if requesting_user_name is None:
278            user_jobs = jobs
279        else:
280            user_jobs = [job for job in jobs if job.creator == requesting_user_name]
[ee8e6d0]281       
[b01b6d1]282        return user_jobs
[ee8e6d0]283
[b01b6d1]284    def print_uri(self):
285        pass
286
287    def create_job(self, requesting_user_name="", job_name="", job_k_octets=0):
[eee389a]288        job_id = self._next_job_id
289        self._next_job_id += 1
[ee8e6d0]290       
[b01b6d1]291        job = Job(job_id,
292                  self,
293                  creator=requesting_user_name,
294                  name=job_name,
295                  size=job_k_octets)
296       
[ee8e6d0]297        self.jobs[job_id] = job
[eee389a]298        self.pending_jobs.append(job_id)
[b01b6d1]299       
[ee8e6d0]300        return job
[776a659]301
[b01b6d1]302    def pause_printer(self):
[ee8e6d0]303        pass
[776a659]304
[b01b6d1]305    def resume_printer(self):
306        pass
[776a659]307
[b01b6d1]308    def get_printer_attributes(self, requested_attributes=None):
309        if requested_attributes is None:
310            requested = self.attributes
[e58af05]311        else:
[b01b6d1]312            requested = [a for a in self.attributes if a in requested_attributes]
[b2e077a]313
[b01b6d1]314        _attributes = [attr.replace("-", "_") for attr in requested]
315        attributes = [getattr(self, attr) for attr in _attributes]
316        return attributes
[776a659]317
[b01b6d1]318    def set_printer_attributes(self):
319        pass
Note: See TracBrowser for help on using the repository browser.