[Python-modules-commits] [python-odf] 01/01: New upstream version 1.3.6
Georges Khaznadar
georgesk at moszumanska.debian.org
Sat Jan 13 16:49:36 UTC 2018
This is an automated email from the git hooks/post-receive script.
georgesk pushed a commit to branch upstream
in repository python-odf.
commit e75ca7ff8ed08e9d4fa88329c7bcf2086b99334b
Author: Georges Khaznadar <georgesk at debian.org>
Date: Sat Jan 13 17:49:17 2018 +0100
New upstream version 1.3.6
---
.gitignore | 3 ++
.travis.yml | 10 +++++
HOWTODIST | 13 ++++--
README.md | 12 +++++-
examples/odtmerge.py | 95 ++++++++++++++++++++++++++++++++++++++++
examples/passwd-as-ods.py | 57 +++++++++++-------------
odf/element.py | 107 +++++++++++++++++++++++++++++++++++-----------
odf/namespaces.py | 2 +-
odf/opendocument.py | 4 +-
setup | 2 -
setup.cfg | 7 +++
setup.py | 2 +-
tests/Makefile | 10 -----
tests/runtests | 12 ------
tests/teststyles.py | 4 +-
tests/testunicode.py | 14 +++++-
tox.ini | 12 ++++++
17 files changed, 276 insertions(+), 90 deletions(-)
diff --git a/.gitignore b/.gitignore
index b92d662..688bd6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
build/
*.pyc
+.tox/
+.cache/
+*.egg-info/
diff --git a/.travis.yml b/.travis.yml
index d1ad0ae..67cb5a4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1 +1,11 @@
language: python
+python:
+ - 2.7
+ - 3.3
+ - 3.4
+ - 3.5
+ - 3.6
+install:
+ - pip install tox-travis
+script:
+ - tox
diff --git a/HOWTODIST b/HOWTODIST
index a5b8b20..291898c 100644
--- a/HOWTODIST
+++ b/HOWTODIST
@@ -11,7 +11,13 @@ $ cd odfpy
Run the automated tests:
-$ cd tests ; make ; cd ..
+Install `tox` via `pip` when running the tests for the first time:
+
+$ pip install tox
+
+Run the tests for all supported python versions:
+
+$ tox
Remove the "dev" marker from the version in setup.py and odf/namespaces.py
@@ -19,8 +25,9 @@ $ vi setup.py odf/namespaces.py
~check in the difference locally~
-$ git tag -a release-1.2.X -m "Tagging the 1.2.X release of the 'odfpy' project."
-$ git push origin release-1.2.X
+$ git ci -a
+$ git tag -a release-1.3.X -m "Tagging the 1.3.X release of the 'odfpy' project."
+$ git push origin release-1.3.X
or:
$ git push origin --tags
diff --git a/README.md b/README.md
index 5eed765..5c5b9dd 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,17 @@ The library is incompatible with PyXML.
## RUNNING TESTS
-To run the tests, `cd` into the tests directory and run `make`.
+Install `tox` via `pip` when running the tests for the first time:
+
+```
+$ pip install tox
+```
+
+Run the tests for all supported python versions:
+
+```
+$ tox
+```
## REDISTRIBUTION LICENSE
diff --git a/examples/odtmerge.py b/examples/odtmerge.py
new file mode 100644
index 0000000..a9fef8c
--- /dev/null
+++ b/examples/odtmerge.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2008 Søren Roug, European Environment Agency
+#
+# This is free software. You may redistribute it under the terms
+# of the Apache license and the GNU General Public License Version
+# 2 or at your option any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Contributor(s): Ramiro Batista da Luz
+#
+
+# Inspired by ods2odt.py
+#
+import sys, getopt
+import zipfile, xml.dom.minidom
+from odf.opendocument import OpenDocumentText, load
+from odf.element import Text
+from odf.text import P
+
+
+def usage():
+ sys.stderr.write("Usage: %s -o outputfile inputfile [inputfile2 inputfile3 ...]\n" % sys.argv[0])
+
+
+def merge(inputfile, textdoc):
+ inputtextdoc = load(inputfile)
+
+ # Need to make a copy of the list because addElement unlinks from the original
+ for meta in inputtextdoc.meta.childNodes[:]:
+ textdoc.meta.addElement(meta)
+
+ for font in inputtextdoc.fontfacedecls.childNodes[:]:
+ textdoc.fontfacedecls.addElement(font)
+
+ for style in inputtextdoc.styles.childNodes[:]:
+ textdoc.styles.addElement(style)
+
+ for autostyle in inputtextdoc.automaticstyles.childNodes[:]:
+ textdoc.automaticstyles.addElement(autostyle)
+
+
+ for scripts in inputtextdoc.scripts.childNodes[:]:
+ textdoc.scripts.addElement(scripts)
+
+ for settings in inputtextdoc.settings.childNodes[:]:
+ textdoc.settings.addElement(settings)
+
+ for masterstyles in inputtextdoc.masterstyles.childNodes[:]:
+ textdoc.masterstyles.addElement(masterstyles)
+
+ for body in inputtextdoc.body.childNodes[:]:
+ textdoc.body.addElement(body)
+
+ textdoc.Pictures = inputtextdoc.Pictures
+ return textdoc
+
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "o:", ["output="])
+ except getopt.GetoptError:
+ usage()
+ sys.exit(2)
+
+ outputfile = None
+
+ for o, a in opts:
+ if o in ("-o", "--output"):
+ outputfile = a
+
+ if outputfile is None:
+ usage()
+ sys.exit(2)
+
+ if len(args) < 2:
+ usage()
+ sys.exit(2)
+
+ inputfiles = args[1:]
+
+ textdoc = OpenDocumentText()
+
+ for inputfile in inputfiles:
+ textdoc = merge(inputfile, textdoc)
+
+ textdoc.save(outputfile)
diff --git a/examples/passwd-as-ods.py b/examples/passwd-as-ods.py
index 4391244..8e43950 100644
--- a/examples/passwd-as-ods.py
+++ b/examples/passwd-as-ods.py
@@ -19,46 +19,41 @@
#
from odf.opendocument import OpenDocumentSpreadsheet
-from odf.style import Style, TextProperties, ParagraphProperties, TableColumnProperties
+from odf.style import (ParagraphProperties, Style, TableColumnProperties,
+ TextProperties)
+from odf.table import Table, TableCell, TableColumn, TableRow
from odf.text import P
-from odf.table import Table, TableColumn, TableRow, TableCell
-
-PWENC = "utf-8"
textdoc = OpenDocumentSpreadsheet()
# Create a style for the table content. One we can modify
# later in the word processor.
-tablecontents = Style(name="Table Contents", family="paragraph")
-tablecontents.addElement(ParagraphProperties(numberlines="false", linenumber="0"))
-tablecontents.addElement(TextProperties(fontweight="bold"))
-textdoc.styles.addElement(tablecontents)
+tablecontents = Style(parent=textdoc.styles,
+ name='Table Contents', family='paragraph')
+ParagraphProperties(parent=tablecontents, numberlines='false', linenumber='0')
+TextProperties(parent=tablecontents, fontweight='bold')
# Create automatic styles for the column widths.
# We want two different widths, one in inches, the other one in metric.
# ODF Standard section 15.9.1
-widthshort = Style(name="Wshort", family="table-column")
-widthshort.addElement(TableColumnProperties(columnwidth="1.7cm"))
-textdoc.automaticstyles.addElement(widthshort)
+widthshort = Style(parent=textdoc.automaticstyles,
+ name='Wshort', family='table-column')
+TableColumnProperties(parent=widthshort, columnwidth='1.7cm')
-widthwide = Style(name="Wwide", family="table-column")
-widthwide.addElement(TableColumnProperties(columnwidth="1.5in"))
-textdoc.automaticstyles.addElement(widthwide)
+widthwide = Style(parent=textdoc.automaticstyles,
+ name='Wwide', family='table-column')
+TableColumnProperties(parent=widthwide, columnwidth='1.5in')
# Start the table, and describe the columns
-table = Table(name="Password")
-table.addElement(TableColumn(numbercolumnsrepeated=4,stylename=widthshort))
-table.addElement(TableColumn(numbercolumnsrepeated=3,stylename=widthwide))
-
-f = open('/etc/passwd')
-for line in f:
- rec = line.strip().split(":")
- tr = TableRow()
- table.addElement(tr)
- for val in rec:
- tc = TableCell()
- tr.addElement(tc)
- p = P(stylename=tablecontents,text=unicode(val,PWENC))
- tc.addElement(p)
-
-textdoc.spreadsheet.addElement(table)
-textdoc.save("passwd.ods")
+table = Table(parent=textdoc.spreadsheet, name='Password')
+TableColumn(parent=table, numbercolumnsrepeated=4, stylename=widthshort)
+TableColumn(parent=table, numbercolumnsrepeated=3, stylename=widthwide)
+
+with open('/etc/passwd') as f:
+ for line in f:
+ rec = line.strip().split(':')
+ tr = TableRow(parent=table)
+ for val in rec:
+ tc = TableCell(parent=tr)
+ p = P(parent=tc, stylename=tablecontents, text=val)
+
+textdoc.save('passwd.ods')
diff --git a/odf/element.py b/odf/element.py
index 7ee9c1c..7dc3e2d 100644
--- a/odf/element.py
+++ b/odf/element.py
@@ -24,6 +24,7 @@
#
import sys, os.path
sys.path.append(os.path.dirname(__file__))
+import re
import xml.dom
from xml.dom.minicompat import *
from odf.namespaces import nsdict
@@ -32,6 +33,60 @@ from odf.attrconverters import AttrConverters
if sys.version_info[0] == 3:
unicode=str # unicode function does not exist
+ unichr=chr # unichr does not exist
+
+_xml11_illegal_ranges = (
+ (0x0, 0x0,),
+ (0xd800, 0xdfff,),
+ (0xfffe, 0xffff,),
+)
+
+_xml10_illegal_ranges = _xml11_illegal_ranges + (
+ (0x01, 0x08,),
+ (0x0b, 0x0c,),
+ (0x0e, 0x1f,),
+)
+
+_xml_discouraged_ranges = (
+ (0x7f, 0x84,),
+ (0x86, 0x9f,),
+)
+
+if sys.maxunicode >= 0x10000:
+ # modern or "wide" python build
+ _xml_discouraged_ranges = _xml_discouraged_ranges + (
+ (0x1fffe, 0x1ffff,),
+ (0x2fffe, 0x2ffff,),
+ (0x3fffe, 0x3ffff,),
+ (0x4fffe, 0x4ffff,),
+ (0x5fffe, 0x5ffff,),
+ (0x6fffe, 0x6ffff,),
+ (0x7fffe, 0x7ffff,),
+ (0x8fffe, 0x8ffff,),
+ (0x9fffe, 0x9ffff,),
+ (0xafffe, 0xaffff,),
+ (0xbfffe, 0xbffff,),
+ (0xcfffe, 0xcffff,),
+ (0xdfffe, 0xdffff,),
+ (0xefffe, 0xeffff,),
+ (0xffffe, 0xfffff,),
+ (0x10fffe, 0x10ffff,),
+ )
+# else "narrow" python build - only possible with old versions
+
+def _range_seq_to_re(range_seq):
+ # range pairs are specified as closed intervals
+ return re.compile(u"[{}]".format(
+ u"".join(
+ u"{}-{}".format(re.escape(unichr(lo)), re.escape(unichr(hi)))
+ for lo, hi in range_seq
+ )
+ ), flags=re.UNICODE)
+
+_xml_filtered_chars_re = _range_seq_to_re(_xml10_illegal_ranges + _xml_discouraged_ranges)
+
+def _handle_unrepresentable(data):
+ return _xml_filtered_chars_re.sub(u"\ufffd", data)
# The following code is pasted form xml.sax.saxutils
# Tt makes it possible to run the code without the xml sax package installed
@@ -50,6 +105,9 @@ def _escape(data, entities={}):
data = data.replace(chars, entity)
return data
+def _sanitize(data, entities={}):
+ return _escape(_handle_unrepresentable(data), entities=entities)
+
def _quoteattr(data, entities={}):
""" Escape and quote an attribute value.
@@ -63,7 +121,7 @@ def _quoteattr(data, entities={}):
"""
entities['\n']='
'
entities['\r']=''
- data = _escape(data, entities)
+ data = _sanitize(data, entities)
if '"' in data:
if "'" in data:
data = '"%s"' % data.replace('"', """)
@@ -259,7 +317,7 @@ class Text(Childless, Node):
def toXml(self,level,f):
""" Write XML in UTF-8 """
if self.data:
- f.write(_escape(unicode(self.data)))
+ f.write(_sanitize(unicode(self.data)))
class CDATASection(Text, Childless):
nodeType = Node.CDATA_SECTION_NODE
@@ -289,7 +347,7 @@ class Element(Node):
Node.TEXT_NODE,
Node.CDATA_SECTION_NODE,
Node.ENTITY_REFERENCE_NODE)
-
+
def __init__(self, attributes=None, text=None, cdata=None, qname=None, qattributes=None, check_grammar=True, **args):
if qname is not None:
self.qname = qname
@@ -340,7 +398,7 @@ class Element(Node):
for ns,p in nsdict.items():
if p == prefix: return ns
return None
-
+
def get_nsprefix(self, namespace):
""" Odfpy maintains a list of known namespaces. In some cases we have a namespace URL,
and needs to look up or assign the prefix for it.
@@ -358,7 +416,7 @@ class Element(Node):
element.ownerDocument = self.ownerDocument
for child in element.childNodes:
self._setOwnerDoc(child)
-
+
def addElement(self, element, check_grammar=True):
""" adds an element to an Element
@@ -416,20 +474,23 @@ class Element(Node):
library will add the correct namespace.
Must overwrite, If attribute already exists.
"""
- allowed_attrs = self.allowed_attributes()
- if allowed_attrs is None:
- if type(attr) == type(()):
- prefix, localname = attr
- self.setAttrNS(prefix, localname, value)
- else:
- raise AttributeError( "Unable to add simple attribute - use (namespace, localpart)")
+ if attr == 'parent' and value is not None:
+ value.addElement(self)
else:
- # Construct a list of allowed arguments
- allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
- if check_grammar and attr not in allowed_args:
- raise AttributeError( "Attribute %s is not allowed in <%s>" % ( attr, self.tagName))
- i = allowed_args.index(attr)
- self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value)
+ allowed_attrs = self.allowed_attributes()
+ if allowed_attrs is None:
+ if type(attr) == type(()):
+ prefix, localname = attr
+ self.setAttrNS(prefix, localname, value)
+ else:
+ raise AttributeError( "Unable to add simple attribute - use (namespace, localpart)")
+ else:
+ # Construct a list of allowed arguments
+ allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
+ if check_grammar and attr not in allowed_args:
+ raise AttributeError( "Attribute %s is not allowed in <%s>" % ( attr, self.tagName))
+ i = allowed_args.index(attr)
+ self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value)
def setAttrNS(self, namespace, localpart, value):
""" Add an attribute to the element
@@ -490,10 +551,10 @@ class Element(Node):
f.write(('<'+self.tagName))
if level == 0:
for namespace, prefix in self.namespaces.items():
- f.write(u' xmlns:' + prefix + u'="'+ _escape(str(namespace))+'"')
+ f.write(u' xmlns:' + prefix + u'="'+ _sanitize(str(namespace))+'"')
for qname in self.attributes.keys():
prefix = self.get_nsprefix(qname[0])
- f.write(u' '+_escape(str(prefix+u':'+qname[1]))+u'='+_quoteattr(unicode(self.attributes[qname])))
+ f.write(u' '+_sanitize(str(prefix+u':'+qname[1]))+u'='+_quoteattr(unicode(self.attributes[qname])))
f.write(u'>')
def write_close_tag(self, level, f):
@@ -508,10 +569,10 @@ class Element(Node):
f.write(u'<'+self.tagName)
if level == 0:
for namespace, prefix in self.namespaces.items():
- f.write(u' xmlns:' + prefix + u'="'+ _escape(str(namespace))+u'"')
+ f.write(u' xmlns:' + prefix + u'="'+ _sanitize(str(namespace))+u'"')
for qname in self.attributes.keys():
prefix = self.get_nsprefix(qname[0])
- f.write(u' '+_escape(unicode(prefix+':'+qname[1]))+u'='+_quoteattr(unicode(self.attributes[qname])))
+ f.write(u' '+_sanitize(unicode(prefix+':'+qname[1]))+u'='+_quoteattr(unicode(self.attributes[qname])))
if self.childNodes:
f.write(u'>')
for element in self.childNodes:
@@ -537,5 +598,3 @@ class Element(Node):
""" This is a check to see if the object is an instance of a type """
obj = element(check_grammar=False)
return self.qname == obj.qname
-
-
diff --git a/odf/namespaces.py b/odf/namespaces.py
index 525a9b4..02fac0a 100644
--- a/odf/namespaces.py
+++ b/odf/namespaces.py
@@ -17,7 +17,7 @@
#
# Contributor(s):
#
-__version__ = "1.3.5"
+__version__ = "1.3.6"
TOOLSVERSION = u"ODFPY/" + __version__
diff --git a/odf/opendocument.py b/odf/opendocument.py
index 9b8191e..1b3f8aa 100644
--- a/odf/opendocument.py
+++ b/odf/opendocument.py
@@ -532,7 +532,7 @@ class OpenDocument:
self.manifest.addElement(manifest.FileEntry(fullpath=u"%s%s" % ( folder ,arcname), mediatype=mediatype))
hasPictures = True
if what_it_is == IS_FILENAME:
- self._z.write(fileobj, arcname, zipfile.ZIP_STORED)
+ self._z.write(fileobj, folder + arcname, zipfile.ZIP_STORED)
else:
zi = zipfile.ZipInfo(str(arcname), self._now)
zi.compress_type = zipfile.ZIP_STORED
@@ -864,7 +864,7 @@ def __loadxmlparts(z, manifest, doc, objectpath):
assert(isinstance(doc, OpenDocument))
assert(type(objectpath)==type(u""))
- from load import LoadParser
+ from odf.load import LoadParser
from xml.sax import make_parser, handler
for xmlfile in (objectpath+u'settings.xml', objectpath+u'meta.xml', objectpath+u'content.xml', objectpath+u'styles.xml'):
diff --git a/setup b/setup
deleted file mode 100644
index 3c6e79c..0000000
--- a/setup
+++ /dev/null
@@ -1,2 +0,0 @@
-[bdist_wheel]
-universal=1
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..1935cf0
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,7 @@
+[bdist_wheel]
+universal=1
+
+[tool:pytest]
+norecursedirs = grammar
+addopts = tests
+python_files = test*.py
diff --git a/setup.py b/setup.py
index 90f0861..e920fb3 100644
--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,7 @@
import platform
from setuptools import setup
-version = '1.3.5'
+version = '1.3.6'
if platform.system() in ('Linux','Unix'):
man1pages = [('share/man/man1', [
diff --git a/tests/Makefile b/tests/Makefile
deleted file mode 100644
index 7d57de3..0000000
--- a/tests/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-all: odf runall
-
-runall:
- ./runtests
-
-odf:
- ln -s ../odf
-
-clean:
- rm -f *.1 *.txt odf
diff --git a/tests/runtests b/tests/runtests
deleted file mode 100755
index ffa3a8f..0000000
--- a/tests/runtests
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-echo '========= Python 2 tests =========='
-for file in test*.py
-do
- python2 $file
-done
-
-echo '========= Python 3 tests =========='
-for file in test*.py
-do
- python3 $file
-done
diff --git a/tests/teststyles.py b/tests/teststyles.py
index e9c318b..cca8b93 100644
--- a/tests/teststyles.py
+++ b/tests/teststyles.py
@@ -88,8 +88,8 @@ class TestQattributes(unittest.TestCase):
textdoc.styles.addElement(standard)
s = textdoc.stylesxml()
s.index(u"""<?xml version='1.0' encoding='UTF-8'?>\n""")
- s.index(u'xmlns:ns41="http://foreignuri.com"')
- s.index(u'<style:paragraph-properties ns41:enable-numbering="true"/>')
+ s.index(u'xmlns:ns44="http://foreignuri.com"')
+ s.index(u'<style:paragraph-properties ns44:enable-numbering="true"/>')
e = ElementParser(s,u'style:style')
# e = ElementParser('<style:style style:name="Standard" style:display-name="Standard" style:family="paragraph">')
self.assertEqual(e.element,u'style:style')
diff --git a/tests/testunicode.py b/tests/testunicode.py
index b3e2cc7..1ac0e60 100644
--- a/tests/testunicode.py
+++ b/tests/testunicode.py
@@ -40,6 +40,8 @@ class TestUnicode(unittest.TestCase):
def assertNotContains(self, stack, needle):
self.assertEqual(-1, stack.find(needle))
+ @unittest.skipIf(sys.version_info[0] != 2,
+ "For Python3, unicode strings are type 'str'.")
def test_xstyle(self):
self.assertRaises(UnicodeDecodeError, style.Style, name="X✗", family="paragraph")
xstyle = style.Style(name=u"X✗", family=u"paragraph")
@@ -61,11 +63,21 @@ class TestUnicode(unittest.TestCase):
p = H(outlinelevel=1,text=u"Æblegrød")
p.addText(u' Blåbærgrød')
self.textdoc.text.addElement(p)
- c = self.textdoc.contentxml() # contentxml is supposed to yeld a bytes
+ c = self.textdoc.contentxml() # contentxml is supposed to yield a bytes
self.assertContains(c, b'<office:body><office:text><text:h text:outline-level="1">\xc3\x86blegr\xc3\xb8d Bl\xc3\xa5b\xc3\xa6rgr\xc3\xb8d</text:h></office:text></office:body>')
self.assertContains(c, b'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"')
self.assertContains(c, b'<office:automatic-styles/>')
+ def test_illegaltext(self):
+ p = H(outlinelevel=1,text=u"Spot \u001e the")
+ p.addText(u' d\u00a3libe\u0000rate \ud801 mistakes\U0002fffe')
+ self.textdoc.text.addElement(p)
+ c = self.textdoc.contentxml() # contentxml is supposed to yield a bytes
+ # unicode replacement char \UFFFD === \xef\xbf\xbd in UTF-8
+ self.assertContains(c, b'<office:body><office:text><text:h text:outline-level="1">Spot \xef\xbf\xbd the d\xc2\xa3libe\xef\xbf\xbdrate \xef\xbf\xbd mistakes\xef\xbf\xbd</text:h></office:text></office:body>')
+ self.assertContains(c, b'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"')
+ self.assertContains(c, b'<office:automatic-styles/>')
+
if __name__ == '__main__':
if sys.version_info[0]==2:
unittest.main()
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..b5b42d4
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,12 @@
+[tox]
+envlist =
+ py27,
+ py34,
+ py35,
+ py36,
+
+[testenv]
+commands =
+ pytest
+deps =
+ pytest
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-odf.git
More information about the Python-modules-commits
mailing list