Python bindings: Re-reading the Pyrex documentation, it is obvious (!) that

Python methods in extension types *can* call cdef methods, so make the
cdef functions I was using, because I hadn't figured this out, be cdef
methods instead. This (a) makes the code simpler/nicer, and (b) will make
it easier to accumulate PAT (and later PMT) over repeated calls of the
(now a) method that reads the next TS packet.

--HG--
extra : convert_revision : svn%3Aeff31bef-be4a-0410-a8fe-e47997df2690/trunk%4097
issue20
tibs 2008-11-23 17:32:28 +00:00
rodzic f16d7ed982
commit 9a493c00b0
1 zmienionych plików z 81 dodań i 85 usunięć

Wyświetl plik

@ -1,8 +1,10 @@
"""tstools.pyx -- Pyrex bindings for the TS tools
This is being developed on a Mac, running OS X. I expect that it will also
build on a Linux machine. I do not expect it to build (as it stands) on
Windows, as it is making assumptions that may not follow thereon.
This is being developed on a Mac, running OS X, and also tested on my Ubuntu
system at work.
I do not expect it to build (as it stands) on Windows, as it is making
assumptions that may not follow thereon.
It is my intent to worry about Windows after it works on the platforms that
I can test most easily!
@ -182,16 +184,6 @@ cdef extern from 'es_fns.h':
class TSToolsException(Exception):
pass
cdef same_ES_unit(ES_unit_p this, ES_unit_p that):
"""Two ES units do not need to be at the same place to be the same.
"""
if this.data_len != that.data_len:
return False
for 0 <= ii < this.data_len:
if this.data[ii] != that.data[ii]:
return False
return True
cdef class ESOffset:
"""An offset within an ES file.
@ -249,6 +241,16 @@ cdef class ESOffset:
else:
return 0
cdef same_ES_unit(ES_unit_p this, ES_unit_p that):
"""Two ES units do not need to be at the same place to be the same.
"""
if this.data_len != that.data_len:
return False
for 0 <= ii < this.data_len:
if this.data[ii] != that.data[ii]:
return False
return True
cdef class ESUnit # Forward declaration
cdef object compare_ESUnits(ESUnit this, ESUnit that, int op):
"""op is 2 for ==, 3 for !=, other values not allowed.
@ -336,40 +338,6 @@ cdef class ESUnit:
else:
raise AttributeError
# Is this the simplest way? Since it appears that a class method
# doesn't want to take a non-Python item as an argument...
cdef _next_ESUnit(ES_p stream, filename):
cdef ES_unit_p unit
# The C function assumes it has a valid ES stream passed to it
# = I don't think we're always called with such
if stream == NULL:
raise TSToolsException,'No ES stream to read'
retval = find_and_build_next_ES_unit(stream, &unit)
if retval == EOF:
raise StopIteration
elif retval != 0:
raise TSToolsException,'Error getting next ES unit from file %s'%filename
# I'd like to be able to do:
# return ESUnit(unit=unit)
# but it's not possible to pass anything other than a Python object
# to methods, and an ES_unit_p is not (which is the point of what we're
# doing!). I could take the innards of the ES unit, and pass them as
# individual arguments, appropriately mangled, but that seems a bit like
# overkill when the (rather inelegant but at least hidden in this factory
# method) approach below actually works.
# Maybe I'll figure out something better as I learn more about Pyrex.
# From http://www.philhassey.com/blog/2007/12/05/pyrex-from-confusion-to-enlightenment/
# Pyrex doesn't do type inference, so it doesn't detect that 'u' is allowed
# to hold an ES_unit_p. It's up to us to *tell* it, specifically, what type
# 'u' is going to be.
cdef ESUnit u
u = ESUnit()
u.unit = unit
return u
cdef class ESFile:
"""A Python class representing an ES stream.
@ -444,12 +412,34 @@ cdef class ESFile:
"""
return self.mode == 'w' and self.file_stream != NULL
cdef _next_ESUnit(self):
cdef ES_unit_p unit
# The C function assumes it has a valid ES stream passed to it
# = I don't think we're always called with such
if self.stream == NULL:
raise TSToolsException,'No ES stream to read'
retval = find_and_build_next_ES_unit(self.stream, &unit)
if retval == EOF:
raise StopIteration
elif retval != 0:
raise TSToolsException,'Error getting next ES unit from file %s'%self.name
# From http://www.philhassey.com/blog/2007/12/05/pyrex-from-confusion-to-enlightenment/
# Pyrex doesn't do type inference, so it doesn't detect that 'u' is allowed
# to hold an ES_unit_p. It's up to us to *tell* it, specifically, what type
# 'u' is going to be.
cdef ESUnit u
u = ESUnit()
u.unit = unit
return u
# For Pyrex classes, we define a __next__ instead of a next method
# in order to form our iterator
def __next__(self):
"""Our iterator interface retrieves the ES units from the stream.
"""
return _next_ESUnit(self.stream,self.name)
return self._next_ESUnit()
def seek(self,*args):
"""Seek to the given 'offset', which should be the start of an ES unit.
@ -484,7 +474,7 @@ cdef class ESFile:
"""Read the next ES unit from this stream.
"""
try:
return _next_ESUnit(self.stream,self.name)
return self._next_ESUnit()
except StopIteration:
raise EOFError
@ -802,36 +792,6 @@ cdef class TSPacket:
else:
raise AttributeError
# Not a method, honest
# (__dealloc__ is not allowed to call Python methods, just in case,
# and Python methods don't seem to be allowed to call __dealloc__,
# since I assume it's not a "real" method)
cdef _TSFile_close_for_read(TS_reader_p *tsreader):
if tsreader != NULL:
retval = close_TS_reader(tsreader)
if retval != 0:
raise TSToolsException,"Error closing file '%s':"\
" %s"%(self.name,strerror(errno))
cdef TSPacket _next_TSPacket(TS_reader_p tsreader, filename):
cdef byte *buffer
if tsreader == NULL:
raise TSToolsException,'No TS stream to read'
retval = read_next_TS_packet(tsreader, &buffer)
if retval == EOF:
raise StopIteration
elif retval == 1:
raise TSToolsException,'Error getting next TS packet from file %s'%filename
# Remember the buffer we get handed a pointer to is transient
# so we need to take a copy of it (which we might as well keep in
# a Python object...)
buffer_str = PyString_FromStringAndSize(<char *>buffer, TS_PACKET_LEN)
try:
return TSPacket(buffer_str)
except TSToolsException, what:
raise TSToolsException,\
'Error getting next TS packet from file %s (%s)'%(filename,what)
cdef class TSFile:
"""A Python class representing a TS file.
@ -877,8 +837,18 @@ cdef class TSFile:
self.name = filename
self.mode = mode
# (__dealloc__ is apparently not allowed to call Python methods,
# and Python methods don't seem to be allowed to call __dealloc__,
# so let's have an intermediary)
cdef _close_for_read(self):
if self.tsreader != NULL:
retval = close_TS_reader(&self.tsreader)
if retval != 0:
raise TSToolsException,"Error closing file '%s':"\
" %s"%(self.name,strerror(errno))
def __dealloc__(self):
_TSFile_close_for_read(&self.tsreader)
self._close_for_read()
#if self.tsreader != NULL:
# retval = close_TS_reader(&self.tsreader)
# if retval != 0:
@ -910,12 +880,40 @@ cdef class TSFile:
#return self.mode == 'w' and self.file_stream != NULL
pass
cdef TSPacket _next_TSPacket(self):
"""Read the next TS packet and return an equivalent TSPacket instance.
``filename`` is given for use in exception messages - it should be the
name of the file we're reading from (using ``tsreader``). Perhaps the
tstools C mechanisms should be enhanced to allow that to be recovered
*from* ``tsreader``.
"""
# XXX Should we update self.PAT if the packet has PID 0?
# XXX See tsreport.c::report_ts for how to do this
cdef byte *buffer
if self.tsreader == NULL:
raise TSToolsException,'No TS stream to read'
retval = read_next_TS_packet(self.tsreader, &buffer)
if retval == EOF:
raise StopIteration
elif retval == 1:
raise TSToolsException,'Error getting next TS packet from file %s'%self.name
# Remember the buffer we get handed a pointer to is transient
# so we need to take a copy of it (which we might as well keep in
# a Python object...)
buffer_str = PyString_FromStringAndSize(<char *>buffer, TS_PACKET_LEN)
try:
return TSPacket(buffer_str)
except TSToolsException, what:
raise TSToolsException,\
'Error getting next TS packet from file %s (%s)'%(self.name,what)
# For Pyrex classes, we define a __next__ instead of a next method
# in order to form our iterator
def __next__(self):
"""Our iterator interface retrieves the TS packets from the stream.
"""
return _next_TSPacket(self.tsreader,self.name)
return self._next_TSPacket()
def seek(self,offset):
"""Seek to the given offset, which should be a multiple of 188.
@ -929,10 +927,8 @@ cdef class TSFile:
def read(self):
"""Read the next TS packet from this stream.
"""
# XXX Should we update self.PAT if the packet has PID 0?
# XXX See tsreport.c::report_ts for how to do this
try:
return _next_TSPacket(self.tsreader,self.name)
return self._next_TSPacket()
except StopIteration:
raise EOFError
@ -984,7 +980,7 @@ cdef class TSFile:
# if retval != 0:
# raise TSToolsException,"Error closing file '%s':"\
# " %s"%(self.name,strerror(errno))
_TSFile_close_for_read(&self.tsreader)
self._close_for_read()
self.name = None
self.mode = None