Commit 7185d795 authored by Ryan Fisher's avatar Ryan Fisher

Merge branch 'pass-tests-python3' into 'master'

Fix and enable unit tests under Python 3

See merge request lscsoft/glue!15
parents 3029d8e8 f0b8ca50
......@@ -34,6 +34,20 @@ test:el7:
- python setup.py install
- make -C test
test:stretch:python3:
stage: test
image: ligo/lalsuite-dev:stretch
variables:
GIT_STRATEGY: none
before_script:
- apt-get update
- apt-get install -y lal-python3
script:
- tar xf *.tar.*
- cd $(find . -type d -maxdepth 1 -name 'lscsoft-glue-*')
- python3 setup.py install
- make -C test PYTHON=python3
deploy:stretch:
stage: deploy
image: ligo/lalsuite-dev:stretch
......
......@@ -29,9 +29,11 @@ A collection of iteration utilities.
"""
import functools
import math
import numpy
import random
import six
from glue import git_version
......@@ -118,7 +120,7 @@ def choices(vals, n):
>>> x = choices(["a", "b", "c"], 2)
>>> y = choices(["1", "2", "3"], 2)
>>> zip(x, y)
>>> list(zip(x, y))
[(('a', 'b'), ('1', '2')), (('a', 'c'), ('1', '3')), (('b', 'c'), ('2', '3'))]
Furthermore, the order of combinations in the output sequence is
......@@ -133,7 +135,7 @@ def choices(vals, n):
>>> X = list(choices(x, 2))
>>> Y = list(choices(x, len(x) - 2))
>>> Y.reverse()
>>> zip(X, Y)
>>> list(zip(X, Y))
[(('a', 'b'), ('c', 'd', 'e')), (('a', 'c'), ('b', 'd', 'e')), (('a', 'd'), ('b', 'c', 'e')), (('a', 'e'), ('b', 'c', 'd')), (('b', 'c'), ('a', 'd', 'e')), (('b', 'd'), ('a', 'c', 'e')), (('b', 'e'), ('a', 'c', 'd')), (('c', 'd'), ('a', 'b', 'e')), (('c', 'e'), ('a', 'b', 'd')), (('d', 'e'), ('a', 'b', 'c'))]
"""
if n == len(vals):
......@@ -221,7 +223,7 @@ def inplace_filter(func, sequence):
Example:
>>> l = range(10)
>>> l = list(range(10))
>>> inplace_filter(lambda x: x > 5, l)
>>> l
[6, 7, 8, 9]
......@@ -293,10 +295,10 @@ def inorder(*iterables, **kwargs):
raise TypeError("invalid keyword argument '%s'" % list(kwargs.keys())[0])
nextvals = {}
for iterable in iterables:
next = iter(iterable).next
next_ = functools.partial(next, iter(iterable))
try:
nextval = next()
nextvals[next] = keyfunc(nextval), nextval, next
nextval = next_()
nextvals[next_] = keyfunc(nextval), nextval, next_
except StopIteration:
pass
if not nextvals:
......@@ -306,23 +308,23 @@ def inorder(*iterables, **kwargs):
select = lambda seq: max(seq, key = lambda elem: elem[0])
else:
select = lambda seq: min(seq, key = lambda elem: elem[0])
values = nextvals.itervalues
values = functools.partial(six.itervalues, nextvals)
if len(nextvals) > 1:
while 1:
_, val, next = select(values())
_, val, next_ = select(values())
yield val
try:
nextval = next()
nextvals[next] = keyfunc(nextval), nextval, next
nextval = next_()
nextvals[next_] = keyfunc(nextval), nextval, next_
except StopIteration:
del nextvals[next]
del nextvals[next_]
if len(nextvals) < 2:
break
# exactly one sequence remains, short circuit and drain it
(_, val, next), = values()
(_, val, next_), = values()
yield val
while 1:
yield next()
yield next_()
#
......
......@@ -29,6 +29,10 @@ LAL-derived code (eg. LALApps programs)
"""
try:
from functools import total_ordering
except ImportError:
from functools32 import total_ordering
import fnmatch
import math
import os
......@@ -66,6 +70,7 @@ __date__ = git_version.date
#
@total_ordering
class LIGOTimeGPS(object):
"""
An object for storing times with nanosecond resolution. LAL defines an
......@@ -200,8 +205,8 @@ class LIGOTimeGPS(object):
Example:
>>> long(LIGOTimeGPS(100.5))
100L
>>> repr(long(LIGOTimeGPS(100.5))) == '100L' if sys.version_info.major < 3 else True
True
"""
return long(self.__seconds)
......@@ -221,7 +226,15 @@ class LIGOTimeGPS(object):
def __hash__(self):
return self.__seconds ^ self.__nanoseconds
def __cmp__(self, other):
def __eq__(self, other):
if not isinstance(other, LIGOTimeGPS):
try:
other = LIGOTimeGPS(other)
except TypeError:
return NotImplemented
return self.ns() == other.ns()
def __lt__(self, other):
"""
Compare a value to a LIGOTimeGPS. If the value being compared
to the LIGOTimeGPS is not also a LIGOTimeGPS, then an attempt
......@@ -241,9 +254,9 @@ class LIGOTimeGPS(object):
other = LIGOTimeGPS(other)
except TypeError:
return NotImplemented
return cmp(self.__seconds, other.seconds) or cmp(self.__nanoseconds, other.nanoseconds)
return self.ns() < other.ns()
def __nonzero__(self):
def __bool__(self):
"""
Return True if the LIGOTimeGPS is nonzero.
......@@ -252,7 +265,10 @@ class LIGOTimeGPS(object):
>>> bool(LIGOTimeGPS(100.5))
True
"""
return self.__seconds or self.__nanoseconds
return bool(self.__seconds) or bool(self.__nanoseconds)
# The old Python 2 name for __bool__.
__nonzero__ = __bool__
# arithmetic
......@@ -341,7 +357,7 @@ class LIGOTimeGPS(object):
# multiplication is commutative
__rmul__ = __mul__
def __div__(self, other):
def __truediv__(self, other):
"""
Divide a LIGOTimeGPS by a number.
......@@ -358,6 +374,13 @@ class LIGOTimeGPS(object):
break
return quotient
# Old Python 2 name for __truediv__
__div__ = __truediv__
def __floordiv__(self, other):
"""Integer division (not implemented)."""
raise NotImplemented
def __mod__(self, other):
"""
Compute the remainder when a LIGOTimeGPS is divided by a number.
......
......@@ -55,8 +55,8 @@ suffix can be retrieved by converting the object to an integer.
Example:
>>> x.table_name
u'process'
>>> print(x.table_name)
process
>>> int(x)
10
......@@ -110,7 +110,7 @@ at least one corresponding ID string has been processed.
Example:
>>> import ilwd
>>> from glue.ligolw import ilwd
>>> "foo_bar_class" in ilwd.__dict__
False
>>> x = ilwd.ilwdchar("foo:bar:0")
......@@ -177,7 +177,7 @@ def get_ilwdchar_class(tbl_name, col_name, namespace = globals()):
Example:
>>> for i in range(10):
... print str(process_id(i))
... print(str(process_id(i)))
...
process:process_id:0
process:process_id:1
......@@ -206,12 +206,9 @@ def get_ilwdchar_class(tbl_name, col_name, namespace = globals()):
# otherwise define a new class, and add it to the cache
#
class new_class(_ilwd.ilwdchar):
__slots__ = ()
table_name, column_name = key
index_offset = len(u"%s:%s:" % key)
new_class.__name__ = cls_name
cls_dict = {'__slots__': (), 'index_offset': len(u"%s:%s:" % key)}
cls_dict['table_name'], cls_dict['column_name'] = key
new_class = type(cls_name, (_ilwd.ilwdchar,), cls_dict)
namespace[cls_name] = new_class
......@@ -251,10 +248,10 @@ class ilwdchar(object):
>>> x = ilwdchar(u"process:process_id:10")
>>> str(x)
'process:process_id:10'
>>> x.table_name
u'process'
>>> x.column_name
u'process_id'
>>> print(x.table_name)
process
>>> print(x.column_name)
process_id
>>> int(x)
10
>>> x.index_offset
......
......@@ -144,8 +144,7 @@ class attributeproxy(property):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> # internally, value is stored as unicode (for XML)
>>> x.getAttribute("Scale")
u'16'
>>> assert x.getAttribute("Scale") == '16'
>>> # deleting an attribute restores the default value if defined
>>> del x.Scale
>>> x.Scale
......@@ -449,11 +448,9 @@ class LLWNameAttr(six.text_type):
>>> x = Test()
>>> x.Name = u"blah"
>>> # internally, suffix has been appended
>>> x.getAttribute("Name")
u'blah:test'
>>> assert x.getAttribute("Name") == 'blah:test'
>>> # but attributeproxy reports original value
>>> x.Name
u'blah'
>>> assert x.Name == u'blah'
"""
def __new__(cls, name):
try:
......
......@@ -33,6 +33,10 @@ interested users.
"""
try:
from functools import total_ordering
except ImportError:
from functools32 import total_ordering
import math
import numpy
import warnings
......@@ -162,22 +166,14 @@ class instrumentsproperty(object):
>>> print(instrumentsproperty.get(None))
None
>>> instrumentsproperty.get(u"")
set([])
>>> instrumentsproperty.get(u" , ,,")
set([])
>>> instrumentsproperty.get(u"H1")
set([u'H1'])
>>> instrumentsproperty.get(u"SWIFT")
set([u'SWIFT'])
>>> instrumentsproperty.get(u"H1L1")
set([u'H1', u'L1'])
>>> instrumentsproperty.get(u"H1L1,")
set([u'H1L1'])
>>> instrumentsproperty.get(u"H1,L1")
set([u'H1', u'L1'])
>>> instrumentsproperty.get(u"H1+L1")
set([u'H1', u'L1'])
>>> assert instrumentsproperty.get(u"") == set()
>>> assert instrumentsproperty.get(u" , ,,") == set()
>>> assert instrumentsproperty.get(u"H1") == {'H1'}
>>> assert instrumentsproperty.get(u"SWIFT") == {'SWIFT'}
>>> assert instrumentsproperty.get(u"H1L1") == {'H1', 'L1'}
>>> assert instrumentsproperty.get(u"H1L1,") == {'H1L1'}
>>> assert instrumentsproperty.get(u"H1,L1") == {'H1', 'L1'}
>>> assert instrumentsproperty.get(u"H1+L1") == {'H1', 'L1'}
"""
if ifos is None:
return None
......@@ -232,18 +228,12 @@ class instrumentsproperty(object):
>>> print(instrumentsproperty.set(None))
None
>>> instrumentsproperty.set(())
u''
>>> instrumentsproperty.set((u"H1",))
u'H1'
>>> instrumentsproperty.set((u"H1",u"H1",u"H1"))
u'H1'
>>> instrumentsproperty.set((u"H1",u"L1"))
u'H1,L1'
>>> instrumentsproperty.set((u"SWIFT",))
u'SWIFT'
>>> instrumentsproperty.set((u"H1L1",))
u'H1L1,'
>>> assert instrumentsproperty.set(()) == ''
>>> assert instrumentsproperty.set((u"H1",)) == 'H1'
>>> assert instrumentsproperty.set((u"H1",u"H1",u"H1")) == 'H1'
>>> assert instrumentsproperty.set((u"H1",u"L1")) == 'H1,L1'
>>> assert instrumentsproperty.set((u"SWIFT",)) == 'SWIFT'
>>> assert instrumentsproperty.set((u"H1L1",)) == 'H1L1,'
"""
if instruments is None:
return None
......@@ -405,10 +395,8 @@ class Process(table.Table.RowType):
>>> x = Process()
>>> x.instruments = (u"H1", u"L1")
>>> x.ifos
u'H1,L1'
>>> x.instruments
set([u'H1', u'L1'])
>>> assert x.ifos == 'H1,L1'
>>> assert x.instruments == {'H1', 'L1'}
"""
__slots__ = tuple(ProcessTable.validcolumns.keys())
......@@ -503,17 +491,12 @@ class ProcessParams(table.Table.RowType):
>>> x = ProcessParams()
>>> x.pyvalue = u"test"
>>> x.type
u'lstring'
>>> x.value
u'test'
>>> x.pyvalue
u'test'
>>> assert x.type == 'lstring'
>>> assert x.value == 'test'
>>> assert x.pyvalue == 'test'
>>> x.pyvalue = 6.
>>> x.type
u'real_8'
>>> x.value
u'6'
>>> assert x.type == 'real_8'
>>> assert x.value == '6'
>>> x.pyvalue
6.0
>>> x.pyvalue = None
......@@ -524,10 +507,8 @@ class ProcessParams(table.Table.RowType):
>>> print(x.pyvalue)
None
>>> x.pyvalue = True
>>> x.type
u'int_4s'
>>> x.value
u'1'
>>> assert(x.type) == 'int_4s'
>>> assert x.value == '1'
>>> x.pyvalue
1
"""
......@@ -654,10 +635,8 @@ class SearchSummary(table.Table.RowType):
>>> x = SearchSummary()
>>> x.instruments = (u"H1", u"L1")
>>> x.ifos
u'H1,L1'
>>> x.instruments
set([u'H1', u'L1'])
>>> assert x.ifos == 'H1,L1'
>>> assert x.instruments == {'H1', 'L1'}
>>> x.in_start = x.out_start = LIGOTimeGPS(0)
>>> x.in_end = x.out_end = LIGOTimeGPS(10)
>>> x.in_segment
......@@ -2036,10 +2015,8 @@ class CoincInspiral(table.Table.RowType):
>>> x = CoincInspiral()
>>> x.instruments = (u"H1", u"L1")
>>> x.ifos
u'H1,L1'
>>> x.instruments
set([u'H1', u'L1'])
>>> assert x.ifos == 'H1,L1'
>>> assert x.instruments == {'H1', 'L1'}
>>> x.end = LIGOTimeGPS(10)
>>> x.end
LIGOTimeGPS(10, 0)
......@@ -2964,7 +2941,7 @@ class SimInspiral(table.Table.RowType):
>>> x.time_geocent = LIGOTimeGPS(6e8)
>>> print(x.time_geocent)
600000000
>>> print(x.end_time_gmst)
>>> print(numpy.around(x.end_time_gmst, 8))
-2238.39417156
"""
__slots__ = tuple(SimInspiralTable.validcolumns.keys())
......@@ -3134,7 +3111,7 @@ class SimBurst(table.Table.RowType):
>>> x.time_geocent = LIGOTimeGPS(6e8)
>>> print(x.time_geocent)
600000000
>>> print(x.time_geocent_gmst)
>>> print(numpy.around(x.time_geocent_gmst, 8))
-2238.39417156
"""
__slots__ = tuple(SimBurstTable.validcolumns.keys())
......@@ -3313,10 +3290,8 @@ class SummValue(table.Table.RowType):
>>> x = SummValue()
>>> x.instruments = (u"H1", u"L1")
>>> x.ifo
u'H1,L1'
>>> x.instruments
set([u'H1', u'L1'])
>>> assert x.ifo == 'H1,L1'
>>> assert x.instruments == {'H1', 'L1'}
>>> x.start = LIGOTimeGPS(0)
>>> x.end = LIGOTimeGPS(10)
>>> x.segment
......@@ -3567,6 +3542,7 @@ class SegmentTable(table.Table):
interncolumns = ("process_id",)
@total_ordering
class Segment(table.Table.RowType):
"""
Example:
......@@ -3662,8 +3638,11 @@ class Segment(table.Table.RowType):
def __abs__(self):
return abs(self.segment)
def __cmp__(self, other):
return cmp(self.segment, other)
def __lt__(self, other):
return self.segment < other
def __eq__(self, other):
return self.segment == other
def __contains__(self, other):
return other in self.segment
......@@ -3725,10 +3704,8 @@ class SegmentDef(table.Table.RowType):
>>> x = SegmentDef()
>>> x.instruments = (u"H1", u"L1")
>>> x.ifos
u'H1,L1'
>>> x.instruments
set([u'H1', u'L1'])
>>> assert x.ifos == 'H1,L1'
>>> assert x.instruments =={'H1', 'L1'}
"""
__slots__ = tuple(SegmentDefTable.validcolumns.keys())
......
......@@ -47,6 +47,7 @@ columns of the table.
import copy
import itertools
import re
from six.moves import zip
import sys
from xml.sax.saxutils import escape as xmlescape
from xml.sax.xmlreader import AttributesImpl
......@@ -87,8 +88,7 @@ def get_table(xmldoc, name):
Example:
>>> import ligolw
>>> import lsctables
>>> from . import ligolw, lsctables
>>> xmldoc = ligolw.Document()
>>> xmldoc.appendChild(ligolw.LIGO_LW()).appendChild(lsctables.New(lsctables.SnglInspiralTable))
[]
......@@ -124,8 +124,7 @@ def reassign_ids(elem):
Example:
>>> import ligolw
>>> import lsctables
>>> from . import ligolw, lsctables
>>> xmldoc = ligolw.Document()
>>> xmldoc.appendChild(ligolw.LIGO_LW()).appendChild(lsctables.New(lsctables.SnglInspiralTable))
[]
......@@ -162,10 +161,8 @@ class Column(ligolw.Column):
>>> tbl.appendChild(TableStream(AttributesImpl({u"Name": u"test"}))) # doctest: +ELLIPSIS
<glue.ligolw.table.TableStream object at ...>
>>> tbl._update_column_info()
>>> col.Name
u'snr'
>>> col.Type
u'real_8'
>>> assert col.Name == 'snr'
>>> assert col.Type == 'real_8'
>>> # append 3 rows (with nothing in them)
>>> tbl.append(tbl.RowType())
>>> tbl.append(tbl.RowType())
......@@ -211,11 +208,9 @@ class Column(ligolw.Column):
to the string. The calling code is responsible for doing the
correct manipulations. Therefore, the assignment operation below
>>> col.Name, col.getAttribute("Name")
(u'snr', u'test:snr')
>>> assert col.Name, col.getAttribute("Name") == ('snr', 'test:snr')
>>> col.Name = col.Name
>>> col.Name, col.getAttribute("Name")
(u'snr', u'snr')
>>> assert col.Name, col.getAttribute("Name") == ('snr', 'snr')
does not preserve the value of the "Name" attribute (though it does
preserve the stripped form reported by the .Name property). This
......@@ -278,7 +273,7 @@ class Column(ligolw.Column):
updated as there are items.
"""
if isinstance(i, slice):
for r, val in itertools.izip(self.parentNode[i], value):
for r, val in zip(self.parentNode[i], value):
setattr(r, self.Name, val)
else:
setattr(self.parentNode[i], self.Name, value)
......@@ -601,8 +596,7 @@ class Table(ligolw.Table, list):
Example:
>>> import ligolw
>>> import lsctables
>>> from . import ligolw, lsctables
>>> xmldoc = ligolw.Document()
>>> xmldoc.appendChild(ligolw.LIGO_LW()).appendChild(lsctables.New(lsctables.SnglInspiralTable))
[]
......@@ -655,7 +649,7 @@ class Table(ligolw.Table, list):
Example:
>>> import lsctables
>>> from . import lsctables
>>> lsctables.ProcessTable.CheckProperties(u"Table", {u"Name": u"process:table"})
True
"""
......@@ -674,7 +668,7 @@ class Table(ligolw.Table, list):
Example:
>>> import lsctables
>>> from . import lsctables
>>> tbl = lsctables.New(lsctables.SnglInspiralTable)
>>> col = tbl.getColumnByName("mass1")
"""
......@@ -702,13 +696,11 @@ class Table(ligolw.Table, list):
Example:
>>> import lsctables
>>> from . import lsctables
>>> process_table = lsctables.New(lsctables.ProcessTable, [])
>>> col = process_table.appendColumn("program")
>>> col.getAttribute("Name")
u'process:program'
>>> col.Name
u'program'
>>> assert col.getAttribute("Name") == 'process:program'
>>> assert col.Name == 'program'
"""
try:
self.getColumnByName(name)
......@@ -862,7 +854,7 @@ class Table(ligolw.Table, list):
Example:
>>> import lsctables
>>> from . import lsctables
>>> for cls in lsctables.TableByName.values(): cls.reset_next_id()
"""
if cls.next_id is not None:
......@@ -888,7 +880,7 @@ class Table(ligolw.Table, list):
Example:
>>> import lsctables
>>> from . import lsctables
>>> tbl = lsctables.New(lsctables.ProcessTable)
>>> print(tbl.sync_next_id())
process:process_id:0
......
......@@ -221,9 +221,16 @@ static struct PyMethodDef methods[] = {
"\n"\
"Example:\n"\
"\n"\
">>> from . import tokenizer\n"\
">>> class Row(object):\n"\
"... pass\n"\
"...\n"\
">>> rows = tokenizer.RowBuilder(Row, [\"time\", \"snr\"])\n"\
">>> for row in rows.append([10, 6.8, 15, 29.1]):\n"\
"... print row.snr\n"\
"..."
"... print(row.snr)\n"\
"...\n"
"6.8\n"\
"29.1"\
},
{NULL,}
};
......@@ -243,7 +250,7 @@ PyTypeObject ligolw_RowBuilder_Type = {
"\n"\
"Example:\n"\
"\n"\
">>> import tokenizer\n"\
">>> from . import tokenizer\n"\
">>> class Row(object):\n"\
"... pass\n"\
"...\n"\
......
......@@ -298,7 +298,7 @@ PyTypeObject ligolw_RowDumper_Type = {
">>> rows[2].status = \"good\"\n" \
">>> rowdumper = RowDumper((\"snr\", \"status\"), (\"%.16g\".__mod__, \"\\\"%s\\\"\".__mod__))\n" \
">>> for line in rowdumper.dump(rows):\n" \
"... print line\n" \
"... print(line)\n" \
"... \n" \
"10.1,\"bad\"\n" \
"15.2,\"bad\"\n" \
......
......@@ -348,8 +348,8 @@ def load_fileobj(fileobj, gz = None, xmldoc = None, contenthandler = None):
Example:
>>> from glue.ligolw import ligolw
>>> import StringIO
>>> f = StringIO.StringIO('<?xml version="1.0" encoding="utf-8" ?><!DOCTYPE LIGO_LW SYSTEM "http://ldas-sw.ligo.caltech.edu/doc/ligolwAPI/html/ligolw_dtd.txt"><LIGO_LW><Table Name="demo:table"><Column Name="name" Type="lstring"/><Column Name="value" Type="real8"/><Stream Name="demo:table" Type="Local" Delimiter=",">"mass",0.5,"velocity",34</Stream></Table></LIGO_LW>')
>>> from io import BytesIO
>>> f = BytesIO(b'<?xml version="1.0" encoding="utf-8" ?><!DOCTYPE LIGO_LW SYSTEM "http://ldas-sw.ligo.caltech.edu/doc/ligolwAPI/html/ligolw_dtd.txt"><LIGO_LW><Table Name="demo:table"><Column Name="name" Type="lstring"/><Column Name="value" Type="real8"/><Stream Name="demo:table" Type="Local" Delimiter=",">"mass",0.5,"velocity",34</Stream></Table></LIGO_LW>')
>>> xmldoc, digest = load_fileobj(f, contenthandler = ligolw.LIGOLWContentHandler)
>>> digest
'6bdcc4726b892aad913531684024ed8e'
......@@ -397,6 +397,12 @@ def load_filename(filename, verbose = False, **kwargs):
fileobj = open(filename, "rb")
else:
fileobj = sys.stdin
# In Python 3, ``sys.stdin`` has an attribute called ``buffer`` that is the
# underyling byte-oriented stream.
try:
fileobj = fileobj.buffer
except AttributeError:
pass
xmldoc, hexdigest = load_fileobj(fileobj, **kwargs)
if verbose:
sys.stderr.write("md5sum: %s %s\n" % (hexdigest, (filename if filename is not None else "")))
......@@ -429,6 +435,12 @@ def load_url(url, verbose = False, **kwargs):
fileobj = urllib.request.urlopen(url)
else:
fileobj = sys.stdin
# In Python 3, ``sys.stdin`` has an attribute called ``buffer`` that is the
# underyling byte-oriented stream.
try:
fileobj = fileobj.buffer
except AttributeError:
pass
xmldoc, hexdigest = load_fileobj(fileobj, **kwargs)
if verbose:
sys.stderr.write("md5sum: %s %s\n" % (hexdigest, (url if url is not None else "")))
......@@ -448,10 +460,13 @@ def write_fileobj(xmldoc, fileobj, gz = False, compresslevel = 3, **kwargs):
Example:
>>> from io import BytesIO
>>> import sys
>>> from glue.ligolw import ligolw
>>> xmldoc = load_filename("demo.xml", contenthandler = ligolw.LIGOLWContentHandler)
>>> digest = write_fileobj(xmldoc, sys.stdout) # doctest: +NORMALIZE_WHITESPACE
>>> f = BytesIO()
>>> digest = write_fileobj(xmldoc, f)
>>> print(f.getvalue().decode()) # doctest: +NORMALIZE_WHITESPACE
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE LIGO_LW SYSTEM "http://ldas-sw.ligo.caltech.edu/doc/ligolwAPI/html/ligolw_dtd.txt">
<LIGO_LW>
......@@ -539,7 +554,14 @@ def write_filename(xmldoc, filename, verbose = False, gz = False, with_mv = True
sys.stderr.write("writing %s ...\n" % (("'%s'" % filename) if filename is not None else "stdout"))
with SignalsTrap(trap_signals):
if filename is None:
hexdigest = write_fileobj(xmldoc, sys.stdout, gz = gz, **kwargs)
# In Python 3, ``sys.stdout`` has an attribute called ``buffer`` that is
# the underyling byte-oriented stream.
fileobj = sys.stdout
try:
fileobj = fileobj.buffer
except AttributeError:
pass
hexdigest = write_fileobj(xmldoc, fileobj, gz = gz, **kwargs)
else:
if not gz and filename.endswith(".gz"):
warnings.warn("filename '%s' ends in '.gz' but file is not being gzip-compressed" % filename, UserWarning)
......
......@@ -132,7 +132,7 @@ def compare_table_cols(a, b):
(ignoring order) according to LIGO LW name conventions, return True
otherwise.
"""
return cmp(sorted((col.Name, col.Type) for col in a.getElementsByTagName(ligolw.Column.tagName)), sorted((col.Name, col.Type) for col in b.getElementsByTagName(ligolw.Column.tagName)))
return {(col.Name, col.Type) for col in a.getElementsByTagName(ligolw.Column.tagName)} != {(col.Name, col.Type) for col in b.getElementsByTagName(ligolw.Column.tagName)}
def merge_compatible_tables(elem):
......
......@@ -404,8 +404,7 @@ class LigolwSegments(set):
</Table>
</LIGO_LW>
>>> xmlsegments = LigolwSegments(xmldoc)
>>> xmlsegments.get_by_name("test")
{u'H1': [segment(LIGOTimeGPS(0, 0), LIGOTimeGPS(10, 0))], u'L1': [segment(LIGOTimeGPS(5, 0), LIGOTimeGPS(15, 0))]}
>>> assert xmlsegments.get_by_name("test") == {'H1': [segment(LIGOTimeGPS(0, 0), LIGOTimeGPS(10, 0))], 'L1': [segment(LIGOTimeGPS(5, 0), LIGOTimeGPS(15, 0))]}
>>> xmlsegments.get_by_name("wrong name")
Traceback (most recent call last):
...
......
......@@ -94,8 +94,7 @@ class offsetvector(dict):
Example:
>>> x = offsetvector({"H1": 0, "L1": 10, "V1": 20})
>>> x.deltas
{('H1', 'L1'): 10, ('H1', 'V1'): 20, ('H1', 'H1'): 0}
>>> assert x.deltas == {('H1', 'L1'): 10, ('H1', 'V1'): 20, ('H1', 'H1'): 0}
>>> y = offsetvector({'H1': 100, 'L1': 110, 'V1': 120})
>>> y.deltas == x.deltas
True
......@@ -148,15 +147,14 @@ class offsetvector(dict):
Example:
>>> a = offsetvector({"H1": -10.1234567, "L1": 0.1})
>>> repr(a)
"offsetvector({'H1': -10.1234567, 'L1': 0.1})"
>>> b = eval(repr(a))
>>> b
offsetvector({'H1': -10.1234567, 'L1': 0.1})
>>> b == a
True
>>> b is a
False
>>> c = offsetvector({"H1": -10.1234567})
>>> repr(c)
"offsetvector({'H1': -10.1234567})"
"""
return "%s(%s)" % (self.__class__.__name__, dict.__repr__(self))
......@@ -258,11 +256,9 @@ class offsetvector(dict):
Example:
>>> a = offsetvector({"H1": -10, "H2": -10, "L1": -10})
>>> a.normalize(L1 = 0)
offsetvector({'H2': 0, 'H1': 0, 'L1': 0})