[nvda-addons] Re: commit/readFeeds: norrumar: Initial commit.

  • From: Noelia <nrm1977@xxxxxxxxx>
  • To: nvda-addons@xxxxxxxxxxxxx
  • Date: Sat, 13 Jul 2013 15:17:23 +0200

Hello. I have pushed the initial commit for ReadFeeds addon. Some points:
1. It is in development, without documentation, since it must to be
converted to markdown format, and changes could still be added.
2. I have sent the version found in italian site, with the
corresponding channels, which can be also modified.
3. I have indicated my name as author, but other contributors should
add his or her name too.
Thanks.

2013/7/13, commits-noreply@xxxxxxxxxxxxx <commits-noreply@xxxxxxxxxxxxx>:
> 1 new commit in readFeeds:
>
> https://bitbucket.org/nvdaaddonteam/readfeeds/commits/e15c9cb7fbe9/
> Changeset:   e15c9cb7fbe9
> Branch:      master
> User:        norrumar
> Date:        2013-07-13 15:08:24
> Summary:     Initial commit.
>
> Affected #:  38 files
>
> diff --git a/.gitattributes b/.gitattributes
> new file mode 100644
> index 0000000..ceae4de
> --- /dev/null
> +++ b/.gitattributes
> @@ -0,0 +1,2 @@
> +# Set default behaviour, in case users don't have core.autocrlf set.
> +* text=auto
>
> diff --git a/.gitignore b/.gitignore
> new file mode 100644
> index 0000000..6d124be
> --- /dev/null
> +++ b/.gitignore
> @@ -0,0 +1,10 @@
> +addon/doc/*.css
> +addon/doc/en/
> +*_docHandler.py
> +*.html
> +*.ini
> +*.mo
> +*.pot
> +*.pyc
> +*.nvda-addon
> +.sconsign.dblite
>
> diff --git a/addon/globalPlugins/RSS/Blog SpazioAusili.txt
> b/addon/globalPlugins/RSS/Blog SpazioAusili.txt
> new file mode 100644
> index 0000000..4e93538
> --- /dev/null
> +++ b/addon/globalPlugins/RSS/Blog SpazioAusili.txt   
> @@ -0,0 +1 @@
> +http://www.spazioausili.net/rss/blog
> \ No newline at end of file
>
> diff --git a/addon/globalPlugins/RSS/Forum SpazioAusili.txt
> b/addon/globalPlugins/RSS/Forum SpazioAusili.txt
> new file mode 100644
> index 0000000..467e223
> --- /dev/null
> +++ b/addon/globalPlugins/RSS/Forum SpazioAusili.txt  
> @@ -0,0 +1 @@
> +http://www.spazioausili.net/rss/forum
> \ No newline at end of file
>
> diff --git a/addon/globalPlugins/RSS/W3C-news.txt
> b/addon/globalPlugins/RSS/W3C-news.txt
> new file mode 100644
> index 0000000..d6d78cd
> --- /dev/null
> +++ b/addon/globalPlugins/RSS/W3C-news.txt
> @@ -0,0 +1 @@
> +http://www.w3.org/News/atom.xml
> \ No newline at end of file
>
> diff --git a/addon/globalPlugins/RSS/addons NVDA-project IT RSS.txt
> b/addon/globalPlugins/RSS/addons NVDA-project IT RSS.txt
> new file mode 100644
> index 0000000..ce662ed
> --- /dev/null
> +++ b/addon/globalPlugins/RSS/addons NVDA-project IT RSS.txt  
> @@ -0,0 +1 @@
> +http://addons.nvda-project.org/index.it.rss
> \ No newline at end of file
>
> diff --git a/addon/globalPlugins/RSS/addressFile.txt
> b/addon/globalPlugins/RSS/addressFile.txt
> new file mode 100644
> index 0000000..467e223
> --- /dev/null
> +++ b/addon/globalPlugins/RSS/addressFile.txt
> @@ -0,0 +1 @@
> +http://www.spazioausili.net/rss/forum
> \ No newline at end of file
>
> diff --git a/addon/globalPlugins/readFeeds.py
> b/addon/globalPlugins/readFeeds.py
> new file mode 100644
> index 0000000..42ae39f
> --- /dev/null
> +++ b/addon/globalPlugins/readFeeds.py
> @@ -0,0 +1,388 @@
> +# -*- coding: UTF-8 -*-
> +
> +# Read feeds: A simple plugin for reading feeds with NVDA
> +# Version: 5.0
> +# Control instead of alt in gesture. Added installTask to update the
> add-on
> +# Version: 4.2
> +# Channel title and number of articles in News list dialog
> +# Date: 23/06/2012
> +
> +
> +import addonHandler
> +import globalPluginHandler
> +import languageHandler
> +import os
> +import sys
> +import shutil
> +import config
> +import urllib
> +import scriptHandler
> +import api
> +import gui
> +import wx
> +import ui
> +
> +sys.path.append(os.path.dirname(__file__))
> +from xml2.dom import minidom
> +del sys.path[-1]
> +
> +addonHandler.initTranslation()
> +
> +address = 'http://rss.slashdot.org/Slashdot/slashdot' # Default address
> +_savePath = os.path.join(os.path.dirname(__file__), "RSS")
> +addressFile = os.path.join(_savePath, "addressFile.txt")
> +
> +try:
> +     f = open(addressFile, "r")
> +     address = f.read()
> +     f.close()
> +except IOError:
> +     pass
> +
> +
> +class Feed:
> +
> +     def __init__(self, range):
> +             # The URL of the RSS feed
> +             # Our actual XML document
> +             try:
> +                     document = minidom.parse(urllib.urlopen(address))
> +             except IOError:
> +                     self.counter = -1
> +                     self.titlesList = []
> +                     self.title = ""
> +                     self.linksList = []
> +                     self.link = ""
> +                     self.channelName = ""
> +                     return
> +             index = 0
> +             self.counter = 0
> +             self.titlesList = []
> +             self.linksList = []
> +             channel = document.getElementsByTagName('channel')
> +             if len(channel)==1:
> +                     self.RSSArticles = True
> +             else:
> +                     self.RSSArticles = False
> +                     channel = document.getElementsByTagName('feed')
> +             channelTitle = 
> channel[0].getElementsByTagName('title')[0].firstChild
> +             if channelTitle is None:
> +                     self.channelName = ""
> +             else:
> +                     self.channelName = channelTitle.data
> +             # if self.channelName is None or self.channelName == "":
> +                     # ui.message(_("Untitled feed"))
> +             if self.RSSArticles:
> +                     articles = document.getElementsByTagName('item')
> +             else:
> +                     articles = document.getElementsByTagName('entry')
> +                     if len(articles) == 0:
> +                             self.counter = -1
> +                             return
> +             for item in articles:
> +                     self.counter +=1
> +                     title = 
> item.getElementsByTagName('title')[0].firstChild.data
> +                     if title is None or title == "":
> +                             self.counter = -1
> +                             ui.message(_("This feed contains untitled 
> items"))
> +                             return
> +                     self.titlesList.append(title)
> +                     if self.RSSArticles:
> +                             link = 
> item.getElementsByTagName('link')[0].firstChild.data
> +                     else:
> +                             link = 
> item.getElementsByTagName('link')[0].getAttribute('href')
> +                     if link is None or link == "":
> +                             self.counter = -1
> +                             ui.message(_("This feed contains items without 
> a link"))
> +                             return
> +                     self.linksList.append(link)
> +                     if index <= range:
> +                             self.title = title
> +                             self.link = link
> +                             # self.creator =
> item.getElementsByTagName('dc:creator')[0].firstChild.data
> +                             index +=1
> +
> +
> +class GlobalPlugin(globalPluginHandler.GlobalPlugin):
> +
> +     def __init__(self):
> +             super(globalPluginHandler.GlobalPlugin, self).__init__()
> +             self.menu = gui.mainFrame.sysTrayIcon.menu
> +             self.readFeedsMenu = wx.Menu()
> +             self.mainItem = self.menu.AppendSubMenu(self.readFeedsMenu, 
> _("&Read
> feeds"), "Manage RSS and Atom feeds")
> +             self.newsListItem = self.readFeedsMenu.Append(wx.ID_ANY, _("News
> &list..."), _("View news list"))
> +             gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onList,
> self.newsListItem)
> +             self.setAddressItem = self.readFeedsMenu.Append(wx.ID_ANY, 
> _("&Temporary
> address..."), _("View or choose current feed address"))
> +             gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onSetAddress,
> self.setAddressItem)
> +             self.setAddressFileItem = self.readFeedsMenu.Append(wx.ID_ANY, 
> _("L&oad
> saved address..."), _("Choose file containing address"))
> +             gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, 
> self.onSetAddressFile,
> self.setAddressFileItem)
> +             self.saveAddressItem = self.readFeedsMenu.Append(wx.ID_ANY, 
> _("Save
> current address..."), _("Copy current address to text file"))
> +             gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onSaveAddress,
> self.saveAddressItem)
> +             self.readFirstItem = self.readFeedsMenu.Append(wx.ID_ANY, 
> _("Updat&e
> current feed"), _("Reload current feed"))
> +             gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, 
> self.onReadFirstFeed,
> self.readFirstItem)
> +             self.copyFeedsItem = self.readFeedsMenu.Append(wx.ID_ANY, 
> _("&Copy feeds
> folder..."), _("Backup of feeds folder"))
> +             gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onCopyFeeds,
> self.copyFeedsItem)
> +             self.restoreFeedsItem = self.readFeedsMenu.Append(wx.ID_ANY, 
> _("R&estore
> feeds..."), _("Restore previously saved feeds"))
> +             gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onRestoreFeeds,
> self.restoreFeedsItem)
> +             self.aboutItem = self.readFeedsMenu.Append(wx.ID_ANY, _("Open
> &documentation"), _("Open documentation for current language"))
> +             gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onAbout,
> self.aboutItem)
> +             self._counter = -1
> +             self._titlesList = []
> +             self._linksList = []
> +             self._index = 0
> +             self._channelName = ""
> +
> +     def terminate(self):
> +             try:
> +                     self.menu.RemoveItem(self.mainItem)
> +             except wx.PyDeadObjectError:
> +                     pass
> +
> +     def onList(self, evt):
> +             _titlesList = self._titlesList
> +             _linksList = self._linksList
> +             if _titlesList == []:
> +                     wx.CallAfter(gui.messageBox, _("Feeds can not be 
> reported. Check your
> Internet conectivity or specified address"), _("Update Error"),
> wx.OK|wx.ICON_ERROR)
> +                     return
> +             dlg = wx.SingleChoiceDialog(gui.mainFrame, _("Open the web page 
> for
> reading your selected article"), "%s (%d)" % (self._channelName,
> len(_titlesList)), choices=_titlesList)
> +             dlg.SetSelection(0)
> +             gui.mainFrame.prePopup()
> +             try:
> +                     result = dlg.ShowModal()
> +             except AttributeError:
> +                     pass
> +             gui.mainFrame.postPopup()
> +             if result == wx.ID_OK:
> +                     self._index = dlg.GetSelection()
> +                     link = _linksList[self._index]
> +                     os.startfile(link)
> +
> +     def onSetAddress(self, evt):
> +             self.setAddressDialog()
> +
> +     def onSetAddressFile(self, evt):
> +             self.setAddressFileDialog()
> +
> +     def onSaveAddress(self, evt):
> +             self.saveAddressDialog()
> +
> +     def onReadFirstFeed(self, evt):
> +             self._index = 0
> +             self._titlesList = []
> +             self._linksList = []
> +             self._channelName = ""
> +             _RSS = Feed(self._index)
> +             self._RSS = _RSS
> +             self._counter = _RSS.counter
> +             self._titlesList = _RSS.titlesList
> +             self._linksList = _RSS.linksList
> +             self._channelName = _RSS.channelName
> +             if self._counter == -1:
> +                     self._titlesList = []
> +                     self._linksList = []
> +                     self._channelName = ""
> +             if self.getCounter() == -1:
> +                     ui.message(_("Feeds can not be reported. Check your 
> Internet conectivity
> or specified address"))
> +                     return
> +             RSS = self._RSS
> +             ui.message(RSS.title)
> +
> +     def onCopyFeeds(self, evt):
> +             configPath = config.getUserDefaultConfigPath()
> +             dlg = wx.DirDialog(gui.mainFrame, _("Select a folder for 
> copying your
> saved feeds"), configPath, wx.DD_DEFAULT_STYLE)
> +             gui.mainFrame.prePopup()
> +             result = dlg.ShowModal()
> +             gui.mainFrame.postPopup()
> +             if result == wx.ID_OK:
> +                     copyPath = os.path.join(dlg.GetPath(), "RSS")
> +                     try:
> +                             shutil.rmtree(copyPath, ignore_errors=True)
> +                             shutil.copytree(_savePath, copyPath)
> +                     except WindowsError:
> +                             wx.CallAfter(gui.messageBox,_("Folder not 
> copied"), _("Copy Error"),
> wx.OK|wx.ICON_ERROR)
> +
> +     def onRestoreFeeds(self, evt):
> +             feedsPath = os.path.join(config.getUserDefaultConfigPath(), 
> "RSS")
> +             dlg = wx.DirDialog(gui.mainFrame, _("Select the feeds folder 
> you wish to
> restore"), feedsPath, wx.DD_DIR_MUST_EXIST | wx.DD_DEFAULT_STYLE)
> +             gui.mainFrame.prePopup()
> +             result = dlg.ShowModal()
> +             gui.mainFrame.postPopup()
> +             if result == wx.ID_OK:
> +                     feedsPath = dlg.GetPath()
> +                     try:
> +                             shutil.rmtree(_savePath, ignore_errors=True)
> +                             shutil.copytree(feedsPath, _savePath)
> +                     except WindowsError:
> +                             wx.CallAfter(gui.messageBox,_("Folder not 
> copied"), _("Copy Error"),
> wx.OK|wx.ICON_ERROR)
> +
> +     def getDocFolder(self):
> +             langs = [languageHandler.getLanguage(), "en"]
> +             for lang in langs:
> +                     docFolder = os.path.join(os.path.dirname(__file__), 
> "..", "doc", lang)
> +                     if os.path.isdir(docFolder):
> +                             return docFolder
> +                     if "_" in lang:
> +                             tryLang = lang.split("_")[0]
> +                             docFolder = 
> os.path.join(os.path.dirname(__file__), "..", "doc",
> tryLang)
> +                             if os.path.isdir(docFolder):
> +                                     return docFolder
> +                             if tryLang == "en":
> +                                     break
> +                     if lang == "en":
> +                             break
> +             return None
> +
> +     def getDocPath(self, docFileName):
> +             docPath = self.getDocFolder()
> +             if docPath is not None:
> +                     docFile = os.path.join(docPath, docFileName)
> +                     if os.path.isfile(docFile):
> +                             docPath = docFile
> +             return docPath
> +
> +     def onAbout(self, evt):
> +             try:
> +                     os.startfile(self.getDocPath("readme.html"))
> +             except WindowsError:
> +                     pass
> +
> +     def getCounter(self):
> +             return self._counter
> +
> +     def getFeed(self):
> +             # feed = ""
> +             feed = self._titlesList[self._index]
> +             return feed
> +
> +     def script_readFirstFeed(self, gesture):
> +             self.onReadFirstFeed(None)
> +     script_readFirstFeed.__doc__ = _("Updates the selected channel and reads
> the first feed title.")
> +
> +     def script_readCurrentFeed(self, gesture):
> +             if self.getCounter() == -1:
> +                     ui.message(_("Feeds can not be reported. Check your 
> Internet conectivity
> or specified address"))
> +                     return
> +             feed = self.getFeed()
> +             link = self._linksList[self._index]
> +             feedLink = "%s\r\n\r\n%s" % (feed, link)
> +             if scriptHandler.getLastScriptRepeatCount()==1 and
> api.copyToClip(feedLink):
> +                     ui.message(_("Copied to clipboard %s") % feedLink)
> +             else:
> +                     ui.message(feed)
> +     script_readCurrentFeed.__doc__ = _("Reads the current feed title. 
> Pressed
> two times, copies the text to clipboard.")
> +
> +     def script_readNextFeed(self, gesture):
> +             if self.getCounter() == -1:
> +                     ui.message(_("Feeds can not be reported. Check your 
> Internet conectivity
> or specified address"))
> +                     return
> +             if self._index >= self.getCounter()-1:
> +                     self._index = 0
> +             else:
> +                     self._index += 1
> +             ui.message(self.getFeed())
> +     script_readNextFeed.__doc__ = _("Reads the next feed title.")
> +
> +     def script_readPriorFeed(self, gesture):
> +             if self.getCounter() == -1:
> +                     ui.message(_("Feeds can not be reported. Check your 
> Internet conectivity
> or specified address"))
> +                     return
> +             if self._index <= 0:
> +                     self._index = self.getCounter()-1
> +             else:
> +                     self._index -=1
> +             ui.message(self.getFeed())
> +     script_readPriorFeed.__doc__ = _("Reads the prior feed title.")
> +
> +     def script_reportLink(self, gesture):
> +             if self.getCounter() == -1:
> +                     ui.message(_("Feeds can not be reported. Check your 
> Internet conectivity
> or specified address"))
> +                     return
> +             feedLink = self._linksList[self._index]
> +             if scriptHandler.getLastScriptRepeatCount()==1:
> +                     os.startfile(feedLink)
> +             else:
> +                     ui.message(feedLink)
> +     script_reportLink.__doc__ = _("Reads the article link, that allows open
> its web page. Pressed two times, opens the web.")
> +
> +     def setAddressDialog(self):
> +             d = wx.TextEntryDialog(gui.mainFrame, _("The address of the 
> channel you
> wish to follow"), _("Address"), defaultValue=address)
> +             def callback(result):
> +                     if result == wx.ID_OK:
> +                             # Make sure this happens after focus returns to 
> the document.
> +                             wx.CallLater(100, self.doSetAddress, 
> d.GetValue())
> +             gui.runScriptModalDialog(d, callback)
> +
> +     def doSetAddress(self, text):
> +             if not text:
> +                     return
> +             global address
> +             address=text
> +             self._index = 0
> +             self._RSS =Feed(self._index)
> +             ui.message(_("Selected %s") % address)
> +
> +     def script_setAddress(self, gesture):
> +             self.onSetAddress(None)
> +     script_setAddress.__doc__ = _("Opens a dialog for setting the address of
> the feeds channel.")
> +
> +     def setAddressFileDialog(self):
> +             d =wx.FileDialog(gui.mainFrame, _("Choose a file containing a 
> channel
> address"), _savePath, "addressFile.txt", _("Text files (*.txt) |*.txt"),
> wx.FD_OPEN)
> +             def callback(result):
> +                     if result == wx.ID_OK:
> +                             # Make sure this happens after focus returns to 
> the document.
> +                             wx.CallLater(100, self.doSetAddressFile, 
> d.GetPath())
> +             gui.runScriptModalDialog(d, callback)
> +
> +     def doSetAddressFile(self, file):
> +             if not file:
> +                     return
> +             global address
> +             try:
> +                     f = open (file, "r")
> +                     address = f.read()
> +                     f.close()
> +                     ui.message(_("Selected %s") % address)
> +             except IOError:
> +                     ui.message(_("Address can not be selected"))
> +                     return
> +             self._index = 0
> +             self._RSS =Feed(self._index)
> +
> +     def script_setAddressFile(self, gesture):
> +             self.onSetAddressFile(None)
> +     script_setAddressFile.__doc__ = _("Opens a dialog for setting the 
> address
> of the feeds channel from a selected file.")
> +
> +     def saveAddressDialog(self):
> +             d =wx.FileDialog(gui.mainFrame, _("File for saving your current
> address"), _savePath, "addressFile.txt", _("Text files (*.txt) | *.txt"),
> wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
> +             def callback(result):
> +                     if result == wx.ID_OK:
> +                             # Make sure this happens after focus returns to 
> the document.
> +                             wx.CallLater(200, self.doSaveAddress, 
> d.GetPath())
> +             gui.runScriptModalDialog(d, callback)
> +
> +     def doSaveAddress(self, file):
> +             if not file:
> +                     return
> +             global address
> +             try:
> +                     f = open (file, "w")
> +                     f.write(address)
> +                     f.close()
> +                     ui.message(_("Saved %s") % address)
> +             except IOError:
> +                     ui.message(_("Address can not be saved"))
> +
> +     def script_saveAddress(self, gesture):
> +             self.onSaveAddress(None)
> +     script_saveAddress.__doc__ = _("Opens a dialog for saving a file
> containing the selected address.")
> +
> +     __gestures = {
> +             "kb:control+shift+NVDA+8": "readFirstFeed",
> +             "kb:control+shift+NVDA+i": "readCurrentFeed",
> +             "kb:control+shift+NVDA+o": "readNextFeed",
> +             "kb:control+shift+NVDA+u": "readPriorFeed",
> +             "kb:control+shift+NVDA+space": "reportLink",
> +             "kb:control+shift+NVDA+enter": "setAddress",
> +             "kb:control+NVDA+enter": "setAddressFile",
> +             "kb:shift+NVDA+enter": "saveAddress",
> +     }
> \ No newline at end of file
>
> diff --git a/addon/globalPlugins/xml2/__init__.py
> b/addon/globalPlugins/xml2/__init__.py
> new file mode 100644
> index 0000000..f7dcb04
> --- /dev/null
> +++ b/addon/globalPlugins/xml2/__init__.py
> @@ -0,0 +1,48 @@
> +"""Core XML support for Python.
> +
> +This package contains four sub-packages:
> +
> +dom -- The W3C Document Object Model.  This supports DOM Level 1 +
> +       Namespaces.
> +
> +parsers -- Python wrappers for XML parsers (currently only supports
> Expat).
> +
> +sax -- The Simple API for XML, developed by XML-Dev, led by David
> +       Megginson and ported to Python by Lars Marius Garshol.  This
> +       supports the SAX 2 API.
> +
> +etree -- The ElementTree XML library.  This is a subset of the full
> +       ElementTree XML release.
> +
> +"""
> +
> +
> +__all__ = ["dom", "parsers", "sax", "etree"]
> +
> +_MINIMUM_XMLPLUS_VERSION = (0, 8, 4)
> +
> +
> +try:
> +    import _xmlplus
> +except ImportError:
> +    pass
> +else:
> +    try:
> +        v = _xmlplus.version_info
> +    except AttributeError:
> +        # _xmlplus is too old; ignore it
> +        pass
> +    else:
> +        if v >= _MINIMUM_XMLPLUS_VERSION:
> +            import sys
> +            _xmlplus.__path__.extend(__path__)
> +            sys.modules[__name__] = _xmlplus
> +        else:
> +            del v
> +
> +import globalPluginHandler
> +
> +class GlobalPlugin(globalPluginHandler.GlobalPlugin):
> +
> +     """Plugin class"""
> +
>
> diff --git a/addon/globalPlugins/xml2/dom/NodeFilter.py
> b/addon/globalPlugins/xml2/dom/NodeFilter.py
> new file mode 100644
> index 0000000..fc05245
> --- /dev/null
> +++ b/addon/globalPlugins/xml2/dom/NodeFilter.py
> @@ -0,0 +1,27 @@
> +# This is the Python mapping for interface NodeFilter from
> +# DOM2-Traversal-Range. It contains only constants.
> +
> +class NodeFilter:
> +    """
> +    This is the DOM2 NodeFilter interface. It contains only constants.
> +    """
> +    FILTER_ACCEPT = 1
> +    FILTER_REJECT = 2
> +    FILTER_SKIP   = 3
> +
> +    SHOW_ALL                    = 0xFFFFFFFFL
> +    SHOW_ELEMENT                = 0x00000001
> +    SHOW_ATTRIBUTE              = 0x00000002
> +    SHOW_TEXT                   = 0x00000004
> +    SHOW_CDATA_SECTION          = 0x00000008
> +    SHOW_ENTITY_REFERENCE       = 0x00000010
> +    SHOW_ENTITY                 = 0x00000020
> +    SHOW_PROCESSING_INSTRUCTION = 0x00000040
> +    SHOW_COMMENT                = 0x00000080
> +    SHOW_DOCUMENT               = 0x00000100
> +    SHOW_DOCUMENT_TYPE          = 0x00000200
> +    SHOW_DOCUMENT_FRAGMENT      = 0x00000400
> +    SHOW_NOTATION               = 0x00000800
> +
> +    def acceptNode(self, node):
> +        raise NotImplementedError
>
> diff --git a/addon/globalPlugins/xml2/dom/__init__.py
> b/addon/globalPlugins/xml2/dom/__init__.py
> new file mode 100644
> index 0000000..6363d00
> --- /dev/null
> +++ b/addon/globalPlugins/xml2/dom/__init__.py
> @@ -0,0 +1,139 @@
> +"""W3C Document Object Model implementation for Python.
> +
> +The Python mapping of the Document Object Model is documented in the
> +Python Library Reference in the section on the xml.dom package.
> +
> +This package contains the following modules:
> +
> +minidom -- A simple implementation of the Level 1 DOM with namespace
> +           support added (based on the Level 2 specification) and other
> +           minor Level 2 functionality.
> +
> +pulldom -- DOM builder supporting on-demand tree-building for selected
> +           subtrees of the document.
> +
> +"""
> +
> +
> +class Node:
> +    """Class giving the NodeType constants."""
> +
> +    # DOM implementations may use this as a base class for their own
> +    # Node implementations.  If they don't, the constants defined here
> +    # should still be used as the canonical definitions as they match
> +    # the values given in the W3C recommendation.  Client code can
> +    # safely refer to these values in all tests of Node.nodeType
> +    # values.
> +
> +    ELEMENT_NODE                = 1
> +    ATTRIBUTE_NODE              = 2
> +    TEXT_NODE                   = 3
> +    CDATA_SECTION_NODE          = 4
> +    ENTITY_REFERENCE_NODE       = 5
> +    ENTITY_NODE                 = 6
> +    PROCESSING_INSTRUCTION_NODE = 7
> +    COMMENT_NODE                = 8
> +    DOCUMENT_NODE               = 9
> +    DOCUMENT_TYPE_NODE          = 10
> +    DOCUMENT_FRAGMENT_NODE      = 11
> +    NOTATION_NODE               = 12
> +
> +
> +#ExceptionCode
> +INDEX_SIZE_ERR                 = 1
> +DOMSTRING_SIZE_ERR             = 2
> +HIERARCHY_REQUEST_ERR          = 3
> +WRONG_DOCUMENT_ERR             = 4
> +INVALID_CHARACTER_ERR          = 5
> +NO_DATA_ALLOWED_ERR            = 6
> +NO_MODIFICATION_ALLOWED_ERR    = 7
> +NOT_FOUND_ERR                  = 8
> +NOT_SUPPORTED_ERR              = 9
> +INUSE_ATTRIBUTE_ERR            = 10
> +INVALID_STATE_ERR              = 11
> +SYNTAX_ERR                     = 12
> +INVALID_MODIFICATION_ERR       = 13
> +NAMESPACE_ERR                  = 14
> +INVALID_ACCESS_ERR             = 15
> +VALIDATION_ERR                 = 16
> +
> +
> +class DOMException(Exception):
> +    """Abstract base class for DOM exceptions.
> +    Exceptions with specific codes are specializations of this class."""
> +
> +    def __init__(self, *args, **kw):
> +        if self.__class__ is DOMException:
> +            raise RuntimeError(
> +                "DOMException should not be instantiated directly")
> +        Exception.__init__(self, *args, **kw)
> +
> +    def _get_code(self):
> +        return self.code
> +
> +
> +class IndexSizeErr(DOMException):
> +    code = INDEX_SIZE_ERR
> +
> +class DomstringSizeErr(DOMException):
> +    code = DOMSTRING_SIZE_ERR
> +
> +class HierarchyRequestErr(DOMException):
> +    code = HIERARCHY_REQUEST_ERR
> +
> +class WrongDocumentErr(DOMException):
> +    code = WRONG_DOCUMENT_ERR
> +
> +class InvalidCharacterErr(DOMException):
> +    code = INVALID_CHARACTER_ERR
> +
> +class NoDataAllowedErr(DOMException):
> +    code = NO_DATA_ALLOWED_ERR
> +
> +class NoModificationAllowedErr(DOMException):
> +    code = NO_MODIFICATION_ALLOWED_ERR
> +
> +class NotFoundErr(DOMException):
> +    code = NOT_FOUND_ERR
> +
> +class NotSupportedErr(DOMException):
> +    code = NOT_SUPPORTED_ERR
> +
> +class InuseAttributeErr(DOMException):
> +    code = INUSE_ATTRIBUTE_ERR
> +
> +class InvalidStateErr(DOMException):
> +    code = INVALID_STATE_ERR
> +
> +class SyntaxErr(DOMException):
> +    code = SYNTAX_ERR
> +
> +class InvalidModificationErr(DOMException):
> +    code = INVALID_MODIFICATION_ERR
> +
> +class NamespaceErr(DOMException):
> +    code = NAMESPACE_ERR
> +
> +class InvalidAccessErr(DOMException):
> +    code = INVALID_ACCESS_ERR
> +
> +class ValidationErr(DOMException):
> +    code = VALIDATION_ERR
> +
> +class UserDataHandler:
> +    """Class giving the operation constants for
> UserDataHandler.handle()."""
> +
> +    # Based on DOM Level 3 (WD 9 April 2002)
> +
> +    NODE_CLONED   = 1
> +    NODE_IMPORTED = 2
> +    NODE_DELETED  = 3
> +    NODE_RENAMED  = 4
> +
> +XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
> +XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
> +XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
> +EMPTY_NAMESPACE = None
> +EMPTY_PREFIX = None
> +
> +from domreg import getDOMImplementation,registerDOMImplementation
>
> diff --git a/addon/globalPlugins/xml2/dom/domreg.py
> b/addon/globalPlugins/xml2/dom/domreg.py
> new file mode 100644
> index 0000000..ea8b7c9
> --- /dev/null
> +++ b/addon/globalPlugins/xml2/dom/domreg.py
> @@ -0,0 +1,99 @@
> +"""Registration facilities for DOM. This module should not be used
> +directly. Instead, the functions getDOMImplementation and
> +registerDOMImplementation should be imported from xml.dom."""
> +
> +from xml2.dom.minicompat import *  # isinstance, StringTypes
> +
> +# This is a list of well-known implementations.  Well-known names
> +# should be published by posting to xml-sig@xxxxxxxxxx, and are
> +# subsequently recorded in this file.
> +
> +well_known_implementations = {
> +    'minidom':'xml.dom.minidom',
> +    '4DOM': 'xml.dom.DOMImplementation',
> +    }
> +
> +# DOM implementations not officially registered should register
> +# themselves with their
> +
> +registered = {}
> +
> +def registerDOMImplementation(name, factory):
> +    """registerDOMImplementation(name, factory)
> +
> +    Register the factory function with the name. The factory function
> +    should return an object which implements the DOMImplementation
> +    interface. The factory function can either return the same object,
> +    or a new one (e.g. if that implementation supports some
> +    customization)."""
> +
> +    registered[name] = factory
> +
> +def _good_enough(dom, features):
> +    "_good_enough(dom, features) -> Return 1 if the dom offers the
> features"
> +    for f,v in features:
> +        if not dom.hasFeature(f,v):
> +            return 0
> +    return 1
> +
> +def getDOMImplementation(name = None, features = ()):
> +    """getDOMImplementation(name = None, features = ()) -> DOM
> implementation.
> +
> +    Return a suitable DOM implementation. The name is either
> +    well-known, the module name of a DOM implementation, or None. If
> +    it is not None, imports the corresponding module and returns
> +    DOMImplementation object if the import succeeds.
> +
> +    If name is not given, consider the available implementations to
> +    find one with the required feature set. If no implementation can
> +    be found, raise an ImportError. The features list must be a sequence
> +    of (feature, version) pairs which are passed to hasFeature."""
> +
> +    import os
> +    creator = None
> +    mod = well_known_implementations.get(name)
> +    if mod:
> +        mod = __import__(mod, {}, {}, ['getDOMImplementation'])
> +        return mod.getDOMImplementation()
> +    elif name:
> +        return registered[name]()
> +    elif "PYTHON_DOM" in os.environ:
> +        return getDOMImplementation(name = os.environ["PYTHON_DOM"])
> +
> +    # User did not specify a name, try implementations in arbitrary
> +    # order, returning the one that has the required features
> +    if isinstance(features, StringTypes):
> +        features = _parse_feature_string(features)
> +    for creator in registered.values():
> +        dom = creator()
> +        if _good_enough(dom, features):
> +            return dom
> +
> +    for creator in well_known_implementations.keys():
> +        try:
> +            dom = getDOMImplementation(name = creator)
> +        except StandardError: # typically ImportError, or AttributeError
> +            continue
> +        if _good_enough(dom, features):
> +            return dom
> +
> +    raise ImportError,"no suitable DOM implementation found"
> +
> +def _parse_feature_string(s):
> +    features = []
> +    parts = s.split()
> +    i = 0
> +    length = len(parts)
> +    while i < length:
> +        feature = parts[i]
> +        if feature[0] in "0123456789":
> +            raise ValueError, "bad feature name: %r" % (feature,)
> +        i = i + 1
> +        version = None
> +        if i < length:
> +            v = parts[i]
> +            if v[0] in "0123456789":
> +                i = i + 1
> +                version = v
> +        features.append((feature, version))
> +    return tuple(features)
>
> diff --git a/addon/globalPlugins/xml2/dom/expatbuilder.py
> b/addon/globalPlugins/xml2/dom/expatbuilder.py
> new file mode 100644
> index 0000000..ddfef11
> --- /dev/null
> +++ b/addon/globalPlugins/xml2/dom/expatbuilder.py
> @@ -0,0 +1,983 @@
> +"""Facility to use the Expat parser to load a minidom instance
> +from a string or file.
> +
> +This avoids all the overhead of SAX and pulldom to gain performance.
> +"""
> +
> +# Warning!
> +#
> +# This module is tightly bound to the implementation details of the
> +# minidom DOM and can't be used with other DOM implementations.  This
> +# is due, in part, to a lack of appropriate methods in the DOM (there is
> +# no way to create Entity and Notation nodes via the DOM Level 2
> +# interface), and for performance.  The later is the cause of some fairly
> +# cryptic code.
> +#
> +# Performance hacks:
> +#
> +#   -  .character_data_handler() has an extra case in which continuing
> +#      data is appended to an existing Text node; this can be a
> +#      speedup since pyexpat can break up character data into multiple
> +#      callbacks even though we set the buffer_text attribute on the
> +#      parser.  This also gives us the advantage that we don't need a
> +#      separate normalization pass.
> +#
> +#   -  Determining that a node exists is done using an identity comparison
> +#      with None rather than a truth test; this avoids searching for and
> +#      calling any methods on the node object if it exists.  (A rather
> +#      nice speedup is achieved this way as well!)
> +
> +from xml2.dom import xmlbuilder, minidom, Node
> +from xml2.dom import EMPTY_NAMESPACE, EMPTY_PREFIX, XMLNS_NAMESPACE
> +from xml2.parsers import expat
> +from xml2.dom.minidom import _append_child, _set_attribute_node
> +from xml2.dom.NodeFilter import NodeFilter
> +
> +from xml2.dom.minicompat import *
> +
> +TEXT_NODE = Node.TEXT_NODE
> +CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE
> +DOCUMENT_NODE = Node.DOCUMENT_NODE
> +
> +FILTER_ACCEPT = xmlbuilder.DOMBuilderFilter.FILTER_ACCEPT
> +FILTER_REJECT = xmlbuilder.DOMBuilderFilter.FILTER_REJECT
> +FILTER_SKIP = xmlbuilder.DOMBuilderFilter.FILTER_SKIP
> +FILTER_INTERRUPT = xmlbuilder.DOMBuilderFilter.FILTER_INTERRUPT
> +
> +theDOMImplementation = minidom.getDOMImplementation()
> +
> +# Expat typename -> TypeInfo
> +_typeinfo_map = {
> +    "CDATA":    minidom.TypeInfo(None, "cdata"),
> +    "ENUM":     minidom.TypeInfo(None, "enumeration"),
> +    "ENTITY":   minidom.TypeInfo(None, "entity"),
> +    "ENTITIES": minidom.TypeInfo(None, "entities"),
> +    "ID":       minidom.TypeInfo(None, "id"),
> +    "IDREF":    minidom.TypeInfo(None, "idref"),
> +    "IDREFS":   minidom.TypeInfo(None, "idrefs"),
> +    "NMTOKEN":  minidom.TypeInfo(None, "nmtoken"),
> +    "NMTOKENS": minidom.TypeInfo(None, "nmtokens"),
> +    }
> +
> +class ElementInfo(object):
> +    __slots__ = '_attr_info', '_model', 'tagName'
> +
> +    def __init__(self, tagName, model=None):
> +        self.tagName = tagName
> +        self._attr_info = []
> +        self._model = model
> +
> +    def __getstate__(self):
> +        return self._attr_info, self._model, self.tagName
> +
> +    def __setstate__(self, state):
> +        self._attr_info, self._model, self.tagName = state
> +
> +    def getAttributeType(self, aname):
> +        for info in self._attr_info:
> +            if info[1] == aname:
> +                t = info[-2]
> +                if t[0] == "(":
> +                    return _typeinfo_map["ENUM"]
> +                else:
> +                    return _typeinfo_map[info[-2]]
> +        return minidom._no_type
> +
> +    def getAttributeTypeNS(self, namespaceURI, localName):
> +        return minidom._no_type
> +
> +    def isElementContent(self):
> +        if self._model:
> +            type = self._model[0]
> +            return type not in (expat.model.XML_CTYPE_ANY,
> +                                expat.model.XML_CTYPE_MIXED)
> +        else:
> +            return False
> +
> +    def isEmpty(self):
> +        if self._model:
> +            return self._model[0] == expat.model.XML_CTYPE_EMPTY
> +        else:
> +            return False
> +
> +    def isId(self, aname):
> +        for info in self._attr_info:
> +            if info[1] == aname:
> +                return info[-2] == "ID"
> +        return False
> +
> +    def isIdNS(self, euri, ename, auri, aname):
> +        # not sure this is meaningful
> +        return self.isId((auri, aname))
> +
> +def _intern(builder, s):
> +    return builder._intern_setdefault(s, s)
> +
> +def _parse_ns_name(builder, name):
> +    assert ' ' in name
> +    parts = name.split(' ')
> +    intern = builder._intern_setdefault
> +    if len(parts) == 3:
> +        uri, localname, prefix = parts
> +        prefix = intern(prefix, prefix)
> +        qname = "%s:%s" % (prefix, localname)
> +        qname = intern(qname, qname)
> +        localname = intern(localname, localname)
> +    else:
> +        uri, localname = parts
> +        prefix = EMPTY_PREFIX
> +        qname = localname = intern(localname, localname)
> +    return intern(uri, uri), localname, prefix, qname
> +
> +
> +class ExpatBuilder:
> +    """Document builder that uses Expat to build a ParsedXML.DOM document
> +    instance."""
> +
> +    def __init__(self, options=None):
> +        if options is None:
> +            options = xmlbuilder.Options()
> +        self._options = options
> +        if self._options.filter is not None:
> +            self._filter =
> FilterVisibilityController(self._options.filter)
> +        else:
> +            self._filter = None
> +            # This *really* doesn't do anything in this case, so
> +            # override it with something fast & minimal.
> +            self._finish_start_element = id
> +        self._parser = None
> +        self.reset()
> +
> +    def createParser(self):
> +        """Create a new parser object."""
> +        return expat.ParserCreate()
> +
> +    def getParser(self):
> +        """Return the parser object, creating a new one if needed."""
> +        if not self._parser:
> +            self._parser = self.createParser()
> +            self._intern_setdefault = self._parser.intern.setdefault
> +            self._parser.buffer_text = True
> +            self._parser.ordered_attributes = True
> +            self._parser.specified_attributes = True
> +            self.install(self._parser)
> +        return self._parser
> +
> +    def reset(self):
> +        """Free all data structures used during DOM construction."""
> +        self.document = theDOMImplementation.createDocument(
> +            EMPTY_NAMESPACE, None, None)
> +        self.curNode = self.document
> +        self._elem_info = self.document._elem_info
> +        self._cdata = False
> +
> +    def install(self, parser):
> +        """Install the callbacks needed to build the DOM into the
> parser."""
> +        # This creates circular references!
> +        parser.StartDoctypeDeclHandler = self.start_doctype_decl_handler
> +        parser.StartElementHandler = self.first_element_handler
> +        parser.EndElementHandler = self.end_element_handler
> +        parser.ProcessingInstructionHandler = self.pi_handler
> +        if self._options.entities:
> +            parser.EntityDeclHandler = self.entity_decl_handler
> +        parser.NotationDeclHandler = self.notation_decl_handler
> +        if self._options.comments:
> +            parser.CommentHandler = self.comment_handler
> +        if self._options.cdata_sections:
> +            parser.StartCdataSectionHandler =
> self.start_cdata_section_handler
> +            parser.EndCdataSectionHandler = self.end_cdata_section_handler
> +            parser.CharacterDataHandler =
> self.character_data_handler_cdata
> +        else:
> +            parser.CharacterDataHandler = self.character_data_handler
> +        parser.ExternalEntityRefHandler = self.external_entity_ref_handler
> +        parser.XmlDeclHandler = self.xml_decl_handler
> +        parser.ElementDeclHandler = self.element_decl_handler
> +        parser.AttlistDeclHandler = self.attlist_decl_handler
> +
> +    def parseFile(self, file):
> +        """Parse a document from a file object, returning the document
> +        node."""
> +        parser = self.getParser()
> +        first_buffer = True
> +        try:
> +            while 1:
> +                buffer = file.read(16*1024)
> +                if not buffer:
> +                    break
> +                parser.Parse(buffer, 0)
> +                if first_buffer and self.document.documentElement:
> +                    self._setup_subset(buffer)
> +                first_buffer = False
> +            parser.Parse("", True)
> +        except ParseEscape:
> +            pass
> +        doc = self.document
> +        self.reset()
> +        self._parser = None
> +        return doc
> +
> +    def parseString(self, string):
> +        """Parse a document from a string, returning the document node."""
> +        parser = self.getParser()
> +        try:
> +            parser.Parse(string, True)
> +            self._setup_subset(string)
> +        except ParseEscape:
> +            pass
> +        doc = self.document
> +        self.reset()
> +        self._parser = None
> +        return doc
> +
> +    def _setup_subset(self, buffer):
> +        """Load the internal subset if there might be one."""
> +        if self.document.doctype:
> +            extractor = InternalSubsetExtractor()
> +            extractor.parseString(buffer)
> +            subset = extractor.getSubset()
> +            self.document.doctype.internalSubset = subset
> +
> +    def start_doctype_decl_handler(self, doctypeName, systemId, publicId,
> +                                   has_internal_subset):
> +        doctype = self.document.implementation.createDocumentType(
> +            doctypeName, publicId, systemId)
> +        doctype.ownerDocument = self.document
> +        _append_child(self.document, doctype)
> +        self.document.doctype = doctype
> +        if self._filter and self._filter.acceptNode(doctype) ==
> FILTER_REJECT:
> +            self.document.doctype = None
> +            del self.document.childNodes[-1]
> +            doctype = None
> +            self._parser.EntityDeclHandler = None
> +            self._parser.NotationDeclHandler = None
> +        if has_internal_subset:
> +            if doctype is not None:
> +                doctype.entities._seq = []
> +                doctype.notations._seq = []
> +            self._parser.CommentHandler = None
> +            self._parser.ProcessingInstructionHandler = None
> +            self._parser.EndDoctypeDeclHandler =
> self.end_doctype_decl_handler
> +
> +    def end_doctype_decl_handler(self):
> +        if self._options.comments:
> +            self._parser.CommentHandler = self.comment_handler
> +        self._parser.ProcessingInstructionHandler = self.pi_handler
> +        if not (self._elem_info or self._filter):
> +            self._finish_end_element = id
> +
> +    def pi_handler(self, target, data):
> +        node = self.document.createProcessingInstruction(target, data)
> +        _append_child(self.curNode, node)
> +        if self._filter and self._filter.acceptNode(node) ==
> FILTER_REJECT:
> +            self.curNode.removeChild(node)
> +
> +    def character_data_handler_cdata(self, data):
> +        childNodes = self.curNode.childNodes
> +        if self._cdata:
> +            if (  self._cdata_continue
> +                  and childNodes[-1].nodeType == CDATA_SECTION_NODE):
> +                childNodes[-1].appendData(data)
> +                return
> +            node = self.document.createCDATASection(data)
> +            self._cdata_continue = True
> +        elif childNodes and childNodes[-1].nodeType == TEXT_NODE:
> +            node = childNodes[-1]
> +            value = node.data + data
> +            d = node.__dict__
> +            d['data'] = d['nodeValue'] = value
> +            return
> +        else:
> +            node = minidom.Text()
> +            d = node.__dict__
> +            d['data'] = d['nodeValue'] = data
> +            d['ownerDocument'] = self.document
> +        _append_child(self.curNode, node)
> +
> +    def character_data_handler(self, data):
> +        childNodes = self.curNode.childNodes
> +        if childNodes and childNodes[-1].nodeType == TEXT_NODE:
> +            node = childNodes[-1]
> +            d = node.__dict__
> +            d['data'] = d['nodeValue'] = node.data + data
> +            return
> +        node = minidom.Text()
> +        d = node.__dict__
> +        d['data'] = d['nodeValue'] = node.data + data
> +        d['ownerDocument'] = self.document
> +        _append_child(self.curNode, node)
> +
> +    def entity_decl_handler(self, entityName, is_parameter_entity, value,
> +                            base, systemId, publicId, notationName):
> +        if is_parameter_entity:
> +            # we don't care about parameter entities for the DOM
> +            return
> +        if not self._options.entities:
> +            return
> +        node = self.document._create_entity(entityName, publicId,
> +                                            systemId, notationName)
> +        if value is not None:
> +            # internal entity
> +            # node *should* be readonly, but we'll cheat
> +            child = self.document.createTextNode(value)
> +            node.childNodes.append(child)
> +        self.document.doctype.entities._seq.append(node)
> +        if self._filter and self._filter.acceptNode(node) ==
> FILTER_REJECT:
> +            del self.document.doctype.entities._seq[-1]
> +
> +    def notation_decl_handler(self, notationName, base, systemId,
> publicId):
> +        node = self.document._create_notation(notationName, publicId,
> systemId)
> +        self.document.doctype.notations._seq.append(node)
> +        if self._filter and self._filter.acceptNode(node) ==
> FILTER_ACCEPT:
> +            del self.document.doctype.notations._seq[-1]
> +
> +    def comment_handler(self, data):
> +        node = self.document.createComment(data)
> +        _append_child(self.curNode, node)
> +        if self._filter and self._filter.acceptNode(node) ==
> FILTER_REJECT:
> +            self.curNode.removeChild(node)
> +
> +    def start_cdata_section_handler(self):
> +        self._cdata = True
> +        self._cdata_continue = False
> +
> +    def end_cdata_section_handler(self):
> +        self._cdata = False
> +        self._cdata_continue = False
> +
> +    def external_entity_ref_handler(self, context, base, systemId,
> publicId):
> +        return 1
> +
> +    def first_element_handler(self, name, attributes):
> +        if self._filter is None and not self._elem_info:
> +            self._finish_end_element = id
> +        self.getParser().StartElementHandler = self.start_element_handler
> +        self.start_element_handler(name, attributes)
> +
> +    def start_element_handler(self, name, attributes):
> +        node = self.document.createElement(name)
> +        _append_child(self.curNode, node)
> +        self.curNode = node
> +
> +        if attributes:
> +            for i in range(0, len(attributes), 2):
> +                a = minidom.Attr(attributes[i], EMPTY_NAMESPACE,
> +                                 None, EMPTY_PREFIX)
> +                value = attributes[i+1]
> +                d = a.childNodes[0].__dict__
> +                d['data'] = d['nodeValue'] = value
> +                d = a.__dict__
> +                d['value'] = d['nodeValue'] = value
> +                d['ownerDocument'] = self.document
> +                _set_attribute_node(node, a)
> +
> +        if node is not self.document.documentElement:
> +            self._finish_start_element(node)
> +
> +    def _finish_start_element(self, node):
> +        if self._filter:
> +            # To be general, we'd have to call isSameNode(), but this
> +            # is sufficient for minidom:
> +            if node is self.document.documentElement:
> +                return
> +            filt = self._filter.startContainer(node)
> +            if filt == FILTER_REJECT:
> +                # ignore this node & all descendents
> +                Rejecter(self)
> +            elif filt == FILTER_SKIP:
> +                # ignore this node, but make it's children become
> +                # children of the parent node
> +                Skipper(self)
> +            else:
> +                return
> +            self.curNode = node.parentNode
> +            node.parentNode.removeChild(node)
> +            node.unlink()
> +
> +    # If this ever changes, Namespaces.end_element_handler() needs to
> +    # be changed to match.
> +    #
> +    def end_element_handler(self, name):
> +        curNode = self.curNode
> +        self.curNode = curNode.parentNode
> +        self._finish_end_element(curNode)
> +
> +    def _finish_end_element(self, curNode):
> +        info = self._elem_info.get(curNode.tagName)
> +        if info:
> +            self._handle_white_text_nodes(curNode, info)
> +        if self._filter:
> +            if curNode is self.document.documentElement:
> +                return
> +            if self._filter.acceptNode(curNode) == FILTER_REJECT:
> +                self.curNode.removeChild(curNode)
> +                curNode.unlink()
> +
> +    def _handle_white_text_nodes(self, node, info):
> +        if (self._options.whitespace_in_element_content
> +            or not info.isElementContent()):
> +            return
> +
> +        # We have element type information and should remove ignorable
> +        # whitespace; identify for text nodes which contain only
> +        # whitespace.
> +        L = []
> +        for child in node.childNodes:
> +            if child.nodeType == TEXT_NODE and not child.data.strip():
> +                L.append(child)
> +
> +        # Remove ignorable whitespace from the tree.
> +        for child in L:
> +            node.removeChild(child)
> +
> +    def element_decl_handler(self, name, model):
> +        info = self._elem_info.get(name)
> +        if info is None:
> +            self._elem_info[name] = ElementInfo(name, model)
> +        else:
> +            assert info._model is None
> +            info._model = model
> +
> +    def attlist_decl_handler(self, elem, name, type, default, required):
> +        info = self._elem_info.get(elem)
> +        if info is None:
> +            info = ElementInfo(elem)
> +            self._elem_info[elem] = info
> +        info._attr_info.append(
> +            [None, name, None, None, default, 0, type, required])
> +
> +    def xml_decl_handler(self, version, encoding, standalone):
> +        self.document.version = version
> +        self.document.encoding = encoding
> +        # This is still a little ugly, thanks to the pyexpat API. ;-(
> +        if standalone >= 0:
> +            if standalone:
> +                self.document.standalone = True
> +            else:
> +                self.document.standalone = False
> +
> +
> +# Don't include FILTER_INTERRUPT, since that's checked separately
> +# where allowed.
> +_ALLOWED_FILTER_RETURNS = (FILTER_ACCEPT, FILTER_REJECT, FILTER_SKIP)
> +
> +class FilterVisibilityController(object):
> +    """Wrapper around a DOMBuilderFilter which implements the checks
> +    to make the whatToShow filter attribute work."""
> +
> +    __slots__ = 'filter',
> +
> +    def __init__(self, filter):
> +        self.filter = filter
> +
> +    def startContainer(self, node):
> +        mask = self._nodetype_mask[node.nodeType]
> +        if self.filter.whatToShow & mask:
> +            val = self.filter.startContainer(node)
> +            if val == FILTER_INTERRUPT:
> +                raise ParseEscape
> +            if val not in _ALLOWED_FILTER_RETURNS:
> +                raise ValueError, \
> +                      "startContainer() returned illegal value: " +
> repr(val)
> +            return val
> +        else:
> +            return FILTER_ACCEPT
> +
> +    def acceptNode(self, node):
> +        mask = self._nodetype_mask[node.nodeType]
> +        if self.filter.whatToShow & mask:
> +            val = self.filter.acceptNode(node)
> +            if val == FILTER_INTERRUPT:
> +                raise ParseEscape
> +            if val == FILTER_SKIP:
> +                # move all child nodes to the parent, and remove this node
> +                parent = node.parentNode
> +                for child in node.childNodes[:]:
> +                    parent.appendChild(child)
> +                # node is handled by the caller
> +                return FILTER_REJECT
> +            if val not in _ALLOWED_FILTER_RETURNS:
> +                raise ValueError, \
> +                      "acceptNode() returned illegal value: " + repr(val)
> +            return val
> +        else:
> +            return FILTER_ACCEPT
> +
> +    _nodetype_mask = {
> +        Node.ELEMENT_NODE:                NodeFilter.SHOW_ELEMENT,
> +        Node.ATTRIBUTE_NODE:              NodeFilter.SHOW_ATTRIBUTE,
> +        Node.TEXT_NODE:                   NodeFilter.SHOW_TEXT,
> +        Node.CDATA_SECTION_NODE:          NodeFilter.SHOW_CDATA_SECTION,
> +        Node.ENTITY_REFERENCE_NODE:
> NodeFilter.SHOW_ENTITY_REFERENCE,
> +        Node.ENTITY_NODE:                 NodeFilter.SHOW_ENTITY,
> +        Node.PROCESSING_INSTRUCTION_NODE:
> NodeFilter.SHOW_PROCESSING_INSTRUCTION,
> +        Node.COMMENT_NODE:                NodeFilter.SHOW_COMMENT,
> +        Node.DOCUMENT_NODE:               NodeFilter.SHOW_DOCUMENT,
> +        Node.DOCUMENT_TYPE_NODE:          NodeFilter.SHOW_DOCUMENT_TYPE,
> +        Node.DOCUMENT_FRAGMENT_NODE:
> NodeFilter.SHOW_DOCUMENT_FRAGMENT,
> +        Node.NOTATION_NODE:               NodeFilter.SHOW_NOTATION,
> +        }
> +
> +
> +class FilterCrutch(object):
> +    __slots__ = '_builder', '_level', '_old_start', '_old_end'
> +
> +    def __init__(self, builder):
> +        self._level = 0
> +        self._builder = builder
> +        parser = builder._parser
> +        self._old_start = parser.StartElementHandler
> +        self._old_end = parser.EndElementHandler
> +        parser.StartElementHandler = self.start_element_handler
> +        parser.EndElementHandler = self.end_element_handler
> +
> +class Rejecter(FilterCrutch):
> +    __slots__ = ()
> +
> +    def __init__(self, builder):
> +        FilterCrutch.__init__(self, builder)
> +        parser = builder._parser
> +        for name in ("ProcessingInstructionHandler",
> +                     "CommentHandler",
> +                     "CharacterDataHandler",
> +                     "StartCdataSectionHandler",
> +                     "EndCdataSectionHandler",
> +                     "ExternalEntityRefHandler",
> +                     ):
> +            setattr(parser, name, None)
> +
> +    def start_element_handler(self, *args):
> +        self._level = self._level + 1
> +
> +    def end_element_handler(self, *args):
> +        if self._level == 0:
> +            # restore the old handlers
> +            parser = self._builder._parser
> +            self._builder.install(parser)
> +            parser.StartElementHandler = self._old_start
> +            parser.EndElementHandler = self._old_end
> +        else:
> +            self._level = self._level - 1
> +
> +class Skipper(FilterCrutch):
> +    __slots__ = ()
> +
> +    def start_element_handler(self, *args):
> +        node = self._builder.curNode
> +        self._old_start(*args)
> +        if self._builder.curNode is not node:
> +            self._level = self._level + 1
> +
> +    def end_element_handler(self, *args):
> +        if self._level == 0:
> +            # We're popping back out of the node we're skipping, so we
> +            # shouldn't need to do anything but reset the handlers.
> +            self._builder._parser.StartElementHandler = self._old_start
> +            self._builder._parser.EndElementHandler = self._old_end
> +            self._builder = None
> +        else:
> +            self._level = self._level - 1
> +            self._old_end(*args)
> +
> +
> +# framework document used by the fragment builder.
> +# Takes a string for the doctype, subset string, and namespace attrs
> string.
> +
> +_FRAGMENT_BUILDER_INTERNAL_SYSTEM_ID = \
> +    "http://xml.python.org/entities/fragment-builder/internal";
> +
> +_FRAGMENT_BUILDER_TEMPLATE = (
> +    '''\
> +<!DOCTYPE wrapper
> +  %%s [
> +  <!ENTITY fragment-builder-internal
> +    SYSTEM "%s">
> +%%s
> +]>
> +<wrapper %%s
> +>&fragment-builder-internal;</wrapper>'''
> +    % _FRAGMENT_BUILDER_INTERNAL_SYSTEM_ID)
> +
> +
> +class FragmentBuilder(ExpatBuilder):
> +    """Builder which constructs document fragments given XML source
> +    text and a context node.
> +
> +    The context node is expected to provide information about the
> +    namespace declarations which are in scope at the start of the
> +    fragment.
> +    """
> +
> +    def __init__(self, context, options=None):
> +        if context.nodeType == DOCUMENT_NODE:
> +            self.originalDocument = context
> +            self.context = context
> +        else:
> +            self.originalDocument = context.ownerDocument
> +            self.context = context
> +        ExpatBuilder.__init__(self, options)
> +
> +    def reset(self):
> +        ExpatBuilder.reset(self)
> +        self.fragment = None
> +
> +    def parseFile(self, file):
> +        """Parse a document fragment from a file object, returning the
> +        fragment node."""
> +        return self.parseString(file.read())
> +
> +    def parseString(self, string):
> +        """Parse a document fragment from a string, returning the
> +        fragment node."""
> +        self._source = string
> +        parser = self.getParser()
> +        doctype = self.originalDocument.doctype
> +        ident = ""
> +        if doctype:
> +            subset = doctype.internalSubset or self._getDeclarations()
> +            if doctype.publicId:
> +                ident = ('PUBLIC "%s" "%s"'
> +                         % (doctype.publicId, doctype.systemId))
> +            elif doctype.systemId:
> +                ident = 'SYSTEM "%s"' % doctype.systemId
> +        else:
> +            subset = ""
> +        nsattrs = self._getNSattrs() # get ns decls from node's ancestors
> +        document = _FRAGMENT_BUILDER_TEMPLATE % (ident, subset, nsattrs)
> +        try:
> +            parser.Parse(document, 1)
> +        except:
> +            self.reset()
> +            raise
> +        fragment = self.fragment
> +        self.reset()
> +##         self._parser = None
> +        return fragment
> +
> +    def _getDeclarations(self):
> +        """Re-create the internal subset from the DocumentType node.
> +
> +        This is only needed if we don't already have the
> +        internalSubset as a string.
> +        """
> +        doctype = self.context.ownerDocument.doctype
> +        s = ""
> +        if doctype:
> +            for i in range(doctype.notations.length):
> +                notation = doctype.notations.item(i)
> +                if s:
> +                    s = s + "\n  "
> +                s = "%s<!NOTATION %s" % (s, notation.nodeName)
> +                if notation.publicId:
> +                    s = '%s PUBLIC "%s"\n             "%s">' \
> +                        % (s, notation.publicId, notation.systemId)
> +                else:
> +                    s = '%s SYSTEM "%s">' % (s, notation.systemId)
> +            for i in range(doctype.entities.length):
> +                entity = doctype.entities.item(i)
> +                if s:
> +                    s = s + "\n  "
> +                s = "%s<!ENTITY %s" % (s, entity.nodeName)
> +                if entity.publicId:
> +                    s = '%s PUBLIC "%s"\n             "%s"' \
> +                        % (s, entity.publicId, entity.systemId)
> +                elif entity.systemId:
> +                    s = '%s SYSTEM "%s"' % (s, entity.systemId)
> +                else:
> +                    s = '%s "%s"' % (s, entity.firstChild.data)
> +                if entity.notationName:
> +                    s = "%s NOTATION %s" % (s, entity.notationName)
> +                s = s + ">"
> +        return s
> +
> +    def _getNSattrs(self):
> +        return ""
> +
> +    def external_entity_ref_handler(self, context, base, systemId,
> publicId):
> +        if systemId == _FRAGMENT_BUILDER_INTERNAL_SYSTEM_ID:
> +            # this entref is the one that we made to put the subtree
> +            # in; all of our given input is parsed in here.
> +            old_document = self.document
> +            old_cur_node = self.curNode
> +            parser = self._parser.ExternalEntityParserCreate(context)
> +            # put the real document back, parse into the fragment to
> return
> +            self.document = self.originalDocument
> +            self.fragment = self.document.createDocumentFragment()
> +            self.curNode = self.fragment
> +            try:
> +                parser.Parse(self._source, 1)
> +            finally:
> +                self.curNode = old_cur_node
> +                self.document = old_document
> +                self._source = None
> +            return -1
> +        else:
> +            return ExpatBuilder.external_entity_ref_handler(
> +                self, context, base, systemId, publicId)
> +
> +
> +class Namespaces:
> +    """Mix-in class for builders; adds support for namespaces."""
> +
> +    def _initNamespaces(self):
> +        # list of (prefix, uri) ns declarations.  Namespace attrs are
> +        # constructed from this and added to the element's attrs.
> +        self._ns_ordered_prefixes = []
> +
> +    def createParser(self):
> +        """Create a new namespace-handling parser."""
> +        parser = expat.ParserCreate(namespace_separator=" ")
> +        parser.namespace_prefixes = True
> +        return parser
> +
> +    def install(self, parser):
> +        """Insert the namespace-handlers onto the parser."""
> +        ExpatBuilder.install(self, parser)
> +        if self._options.namespace_declarations:
> +            parser.StartNamespaceDeclHandler = (
> +                self.start_namespace_decl_handler)
> +
> +    def start_namespace_decl_handler(self, prefix, uri):
> +        """Push this namespace declaration on our storage."""
> +        self._ns_ordered_prefixes.append((prefix, uri))
> +
> +    def start_element_handler(self, name, attributes):
> +        if ' ' in name:
> +            uri, localname, prefix, qname = _parse_ns_name(self, name)
> +        else:
> +            uri = EMPTY_NAMESPACE
> +            qname = name
> +            localname = None
> +            prefix = EMPTY_PREFIX
> +        node = minidom.Element(qname, uri, prefix, localname)
> +        node.ownerDocument = self.document
> +        _append_child(self.curNode, node)
> +        self.curNode = node
> +
> +        if self._ns_ordered_prefixes:
> +            for prefix, uri in self._ns_ordered_prefixes:
> +                if prefix:
> +                    a = minidom.Attr(_intern(self, 'xmlns:' + prefix),
> +                                     XMLNS_NAMESPACE, prefix, "xmlns")
> +                else:
> +                    a = minidom.Attr("xmlns", XMLNS_NAMESPACE,
> +                                     "xmlns", EMPTY_PREFIX)
> +                d = a.childNodes[0].__dict__
> +                d['data'] = d['nodeValue'] = uri
> +                d = a.__dict__
> +                d['value'] = d['nodeValue'] = uri
> +                d['ownerDocument'] = self.document
> +                _set_attribute_node(node, a)
> +            del self._ns_ordered_prefixes[:]
> +
> +        if attributes:
> +            _attrs = node._attrs
> +            _attrsNS = node._attrsNS
> +            for i in range(0, len(attributes), 2):
> +                aname = attributes[i]
> +                value = attributes[i+1]
> +                if ' ' in aname:
> +                    uri, localname, prefix, qname = _parse_ns_name(self,
> aname)
> +                    a = minidom.Attr(qname, uri, localname, prefix)
> +                    _attrs[qname] = a
> +                    _attrsNS[(uri, localname)] = a
> +                else:
> +                    a = minidom.Attr(aname, EMPTY_NAMESPACE,
> +                                     aname, EMPTY_PREFIX)
> +                    _attrs[aname] = a
> +                    _attrsNS[(EMPTY_NAMESPACE, aname)] = a
> +                d = a.childNodes[0].__dict__
> +                d['data'] = d['nodeValue'] = value
> +                d = a.__dict__
> +                d['ownerDocument'] = self.document
> +                d['value'] = d['nodeValue'] = value
> +                d['ownerElement'] = node
> +
> +    if __debug__:
> +        # This only adds some asserts to the original
> +        # end_element_handler(), so we only define this when -O is not
> +        # used.  If changing one, be sure to check the other to see if
> +        # it needs to be changed as well.
> +        #
> +        def end_element_handler(self, name):
> +            curNode = self.curNode
> +            if ' ' in name:
> +                uri, localname, prefix, qname = _parse_ns_name(self, name)
> +                assert (curNode.namespaceURI == uri
> +                        and curNode.localName == localname
> +                        and curNode.prefix == prefix), \
> +                        "element stack messed up! (namespace)"
> +            else:
> +                assert curNode.nodeName == name, \
> +                       "element stack messed up - bad nodeName"
> +                assert curNode.namespaceURI == EMPTY_NAMESPACE, \
> +                       "element stack messed up - bad namespaceURI"
> +            self.curNode = curNode.parentNode
> +            self._finish_end_element(curNode)
> +
> +
> +class ExpatBuilderNS(Namespaces, ExpatBuilder):
> +    """Document builder that supports namespaces."""
> +
> +    def reset(self):
> +        ExpatBuilder.reset(self)
> +        self._initNamespaces()
> +
> +
> +class FragmentBuilderNS(Namespaces, FragmentBuilder):
> +    """Fragment builder that supports namespaces."""
> +
> +    def reset(self):
> +        FragmentBuilder.reset(self)
> +        self._initNamespaces()
> +
> +    def _getNSattrs(self):
> +        """Return string of namespace attributes from this element and
> +        ancestors."""
> +        # XXX This needs to be re-written to walk the ancestors of the
> +        # context to build up the namespace information from
> +        # declarations, elements, and attributes found in context.
> +        # Otherwise we have to store a bunch more data on the DOM
> +        # (though that *might* be more reliable -- not clear).
> +        attrs = ""
> +        context = self.context
> +        L = []
> +        while context:
> +            if hasattr(context, '_ns_prefix_uri'):
> +                for prefix, uri in context._ns_prefix_uri.items():
> +                    # add every new NS decl from context to L and attrs
> string
> +                    if prefix in L:
> +                        continue
> +                    L.append(prefix)
> +                    if prefix:
> +                        declname = "xmlns:" + prefix
> +                    else:
> +                        declname = "xmlns"
> +                    if attrs:
> +                        attrs = "%s\n    %s='%s'" % (attrs, declname, uri)
> +                    else:
> +                        attrs = " %s='%s'" % (declname, uri)
> +            context = context.parentNode
> +        return attrs
> +
> +
> +class ParseEscape(Exception):
> +    """Exception raised to short-circuit parsing in
> InternalSubsetExtractor."""
> +    pass
> +
> +class InternalSubsetExtractor(ExpatBuilder):
> +    """XML processor which can rip out the internal document type
> subset."""
> +
> +    subset = None
> +
> +    def getSubset(self):
> +        """Return the internal subset as a string."""
> +        return self.subset
> +
> +    def parseFile(self, file):
> +        try:
> +            ExpatBuilder.parseFile(self, file)
> +        except ParseEscape:
> +            pass
> +
> +    def parseString(self, string):
> +        try:
> +            ExpatBuilder.parseString(self, string)
> +        except ParseEscape:
> +            pass
> +
> +    def install(self, parser):
> +        parser.StartDoctypeDeclHandler = self.start_doctype_decl_handler
> +        parser.StartElementHandler = self.start_element_handler
> +
> +    def start_doctype_decl_handler(self, name, publicId, systemId,
> +                                   has_internal_subset):
> +        if has_internal_subset:
> +            parser = self.getParser()
> +            self.subset = []
> +            parser.DefaultHandler = self.subset.append
> +            parser.EndDoctypeDeclHandler = self.end_doctype_decl_handler
> +        else:
> +            raise ParseEscape()
> +
> +    def end_doctype_decl_handler(self):
> +        s = ''.join(self.subset).replace('\r\n', '\n').replace('\r', '\n')
> +        self.subset = s
> +        raise ParseEscape()
> +
> +    def start_element_handler(self, name, attrs):
> +        raise ParseEscape()
> +
> +
> +def parse(file, namespaces=True):
> +    """Parse a document, returning the resulting Document node.
> +
> +    'file' may be either a file name or an open file object.
> +    """
> +    if namespaces:
> +        builder = ExpatBuilderNS()
> +    else:
> +        builder = ExpatBuilder()
> +
> +    if isinstance(file, StringTypes):
> +        fp = open(file, 'rb')
> +        try:
> +            result = builder.parseFile(fp)
> +        finally:
> +            fp.close()
> +    else:
> +        result = builder.parseFile(file)
> +    return result
> +
> +
> +def parseString(string, namespaces=True):
> +    """Parse a document from a string, returning the resulting
> +    Document node.
> +    """
> +    if namespaces:
> +        builder = ExpatBuilderNS()
> +    else:
> +        builder = ExpatBuilder()
> +    return builder.parseString(string)
> +
> +
> +def parseFragment(file, context, namespaces=True):
> +    """Parse a fragment of a document, given the context from which it
> +    was originally extracted.  context should be the parent of the
> +    node(s) which are in the fragment.
> +
> +    'file' may be either a file name or an open file object.
> +    """
> +    if namespaces:
> +        builder = FragmentBuilderNS(context)
> +    else:
> +        builder = FragmentBuilder(context)
> +
> +    if isinstance(file, StringTypes):
> +        fp = open(file, 'rb')
> +        try:
> +            result = builder.parseFile(fp)
> +        finally:
> +            fp.close()
> +    else:
> +        result = builder.parseFile(file)
> +    return result
> +
> +
> +def parseFragmentString(string, context, namespaces=True):
> +    """Parse a fragment of a document from a string, given the context
> +    from which it was originally extracted.  context should be the
> +    parent of the node(s) which are in the fragment.
> +    """
> +    if namespaces:
> +        builder = FragmentBuilderNS(context)
> +    else:
> +        builder = FragmentBuilder(context)
> +    return builder.parseString(string)
> +
> +
> +def makeBuilder(options):
> +    """Create a builder based on an Options object."""
> +    if options.namespaces:
> +        return ExpatBuilderNS(options)
> +    else:
> +        return ExpatBuilder(options)
>
> diff --git a/addon/globalPlugins/xml2/dom/minicompat.py
> b/addon/globalPlugins/xml2/dom/minicompat.py
> new file mode 100644
> index 0000000..27299f4
> --- /dev/null
> +++ b/addon/globalPlugins/xml2/dom/minicompat.py
> @@ -0,0 +1,110 @@
> +"""Python version compatibility support for minidom."""
> +
> +# This module should only be imported using "import *".
> +#
> +# The following names are defined:
> +#
> +#   NodeList      -- lightest possible NodeList implementation
> +#
> +#   EmptyNodeList -- lightest possible NodeList that is guaranteed to
> +#                    remain empty (immutable)
> +#
> +#   StringTypes   -- tuple of defined string types
> +#
> +#   defproperty   -- function used in conjunction with GetattrMagic;
> +#                    using these together is needed to make them work
> +#                    as efficiently as possible in both Python 2.2+
> +#                    and older versions.  For example:
> +#
> +#                        class MyClass(GetattrMagic):
> +#                            def _get_myattr(self):
> +#                                return something
> +#
> +#                        defproperty(MyClass, "myattr",
> +#                                    "return some value")
> +#
> +#                    For Python 2.2 and newer, this will construct a
> +#                    property object on the class, which avoids
> +#                    needing to override __getattr__().  It will only
> +#                    work for read-only attributes.
> +#
> +#                    For older versions of Python, inheriting from
> +#                    GetattrMagic will use the traditional
> +#                    __getattr__() hackery to achieve the same effect,
> +#                    but less efficiently.
> +#
> +#                    defproperty() should be used for each version of
> +#                    the relevant _get_<property>() function.
> +
> +__all__ = ["NodeList", "EmptyNodeList", "StringTypes", "defproperty"]
> +
> +import xml2.dom
> +
> +try:
> +    unicode
> +except NameError:
> +    StringTypes = type(''),
> +else:
> +    StringTypes = type(''), type(unicode(''))
> +
> +
> +class NodeList(list):
> +    __slots__ = ()
> +
> +    def item(self, index):
> +        if 0 <= index < len(self):
> +            return self[index]
> +
> +    def _get_length(self):
> +        return len(self)
> +
> +    def _set_length(self, value):
> +        raise xml.dom.NoModificationAllowedErr(
> +            "attempt to modify read-only attribute 'length'")
> +
> +    length = property(_get_length, _set_length,
> +                      doc="The number of nodes in the NodeList.")
> +
> +    def __getstate__(self):
> +        return list(self)
> +
> +    def __setstate__(self, state):
> +        self[:] = state
> +
> +
> +class EmptyNodeList(tuple):
> +    __slots__ = ()
> +
> +    def __add__(self, other):
> +        NL = NodeList()
> +        NL.extend(other)
> +        return NL
> +
> +    def __radd__(self, other):
> +        NL = NodeList()
> +        NL.extend(other)
> +        return NL
> +
> +    def item(self, index):
> +        return None
> +
> +    def _get_length(self):
> +        return 0
> +
> +    def _set_length(self, value):
> +        raise xml.dom.NoModificationAllowedErr(
> +            "attempt to modify read-only attribute 'length'")
> +
> +    length = property(_get_length, _set_length,
> +                      doc="The number of nodes in the NodeList.")
> +
> +
> +def defproperty(klass, name, doc):
> +    get = getattr(klass, ("_get_" + name)).im_func
> +    def set(self, value, name=name):
> +        raise xml.dom.NoModificationAllowedErr(
> +            "attempt to modify read-only attribute " + repr(name))
> +    assert not hasattr(klass, "_set_" + name), \
> +           "expected not to find _set_" + name
> +    prop = property(get, set, doc=doc)
> +    setattr(klass, name, prop)
>
> This diff is so big that we needed to truncate the remainder.
>
> Repository URL: https://bitbucket.org/nvdaaddonteam/readfeeds/
>
> --
>
> This is a commit notification from bitbucket.org. You are receiving
> this because you have the service enabled, addressing the recipient of
> this email.
> ----------------------------------------------------------------
>
> NVDA add-ons Central: A list for discussing NVDA add-ons
>
> To post a message, send an email to nvda-addons@xxxxxxxxxxxxx.
>
> To unsubscribe, send an email with the subject line of "unsubscribe"
> (without quotes) to nvda-addons-request@xxxxxxxxxxxxx.
>
> If you have questions for list moderators, please send a message to
> nvda-addons-moderators@xxxxxxxxxxxxx.
>
> Community addons can be found here: http://addons.nvda-project.org
>
----------------------------------------------------------------

NVDA add-ons Central: A list for discussing NVDA add-ons

To post a message, send an email to nvda-addons@xxxxxxxxxxxxx.

To unsubscribe, send an email with the subject line of "unsubscribe" (without 
quotes) to nvda-addons-request@xxxxxxxxxxxxx.

If you have questions for list moderators, please send a message to 
nvda-addons-moderators@xxxxxxxxxxxxx.

Community addons can be found here: http://addons.nvda-project.org

Other related posts: