home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / schema.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  17.6 KB  |  494 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. """The schema module is responsible for defining what data in the database
  19. gets stored on disk.  
  20.  
  21. The goals of this modules are:
  22.  
  23. * Clearly defining which data from DDBObjects gets stored and which doesn't.
  24. * Validating that all data we write can be read back in
  25. * Making upgrades of the database schema as easy as possible
  26.  
  27. Module-level variables:
  28.     objectSchemas -- Schemas to use with the current database.
  29.     VERSION -- Current schema version.  If you change the schema you must bump
  30.     this number and add a function in the dbupgrade module.
  31.  
  32. Go to the bottom of this file for the current database schema.
  33. """
  34.  
  35. import cPickle
  36. import datetime
  37. import time
  38. import logging
  39. from types import NoneType
  40. from fasttypes import LinkedList
  41. from platformutils import FilenameType
  42.  
  43. class ValidationError(Exception):
  44.     """Error thrown when we try to save invalid data."""
  45.     pass
  46.  
  47. class ValidationWarning(Warning):
  48.     """Warning issued when we try to restore invalid data."""
  49.     pass
  50.  
  51. class SchemaItem(object):
  52.     """SchemaItem represents a single attribute that gets stored on disk.
  53.  
  54.     SchemaItem is an abstract class.  Subclasses of SchemaItem such as
  55.     SchemaAttr, SchemaObject, SchemaList are used in actual object schemas.
  56.  
  57.     Member variables:
  58.         noneOk -- specifies if None is a valid value for this attribute
  59.     """
  60.  
  61.     def __init__(self, noneOk=False):
  62.         self.noneOk = noneOk
  63.  
  64.     def validate(self, data):
  65.         """Validate that data is a valid value for this SchemaItem.
  66.  
  67.         validate is "dumb" when it comes to container types like SchemaList,
  68.         etc.  It only checks that the container is the right type, not its
  69.         children.  This isn't a problem because saveObject() calls
  70.         validate() recursively on all the data it saves, therefore validate
  71.         doesn't have to recirsively validate things.
  72.         """
  73.  
  74.         if data is None:
  75.             if not self.noneOk:
  76.                 raise ValidationError("None value is not allowed")
  77.         return True
  78.  
  79.     def validateType(self, data, correctType):
  80.         """Helper function that many subclasses use"""
  81.         if data is not None and not isinstance(data, correctType):
  82.             raise ValidationError("%r (type: %s) is not a %s" % 
  83.                     (data, type(data), correctType))
  84.  
  85.     def validateTypes(self, data, possibleTypes):
  86.         if data is None:
  87.             return
  88.         for t in possibleTypes:
  89.             if isinstance(data, t):
  90.                 return
  91.         raise ValidationError("%r (type: %s) is not any of: %s" % 
  92.                 (data, type(data), possibleTypes))
  93.  
  94. class SchemaSimpleItem(SchemaItem):
  95.     """Base class for SchemaItems for simple python types."""
  96.  
  97. class SchemaBool(SchemaSimpleItem):
  98.     def validate(self, data):
  99.         super(SchemaSimpleItem, self).validate(data)
  100.         self.validateType(data, bool)
  101.  
  102. class SchemaFloat(SchemaSimpleItem):
  103.     def validate(self, data):
  104.         super(SchemaSimpleItem, self).validate(data)
  105.         self.validateType(data, float)
  106.  
  107. class SchemaString(SchemaSimpleItem):
  108.     def validate(self, data):
  109.         super(SchemaSimpleItem, self).validate(data)
  110.         self.validateType(data, unicode)
  111.  
  112. class SchemaBinary(SchemaSimpleItem):
  113.     def validate(self, data):
  114.         super(SchemaSimpleItem, self).validate(data)
  115.         self.validateType(data, str)
  116.  
  117. class SchemaFilename(SchemaSimpleItem):
  118.     def validate(self, data):
  119.         super(SchemaSimpleItem, self).validate(data)
  120.         self.validateType(data, FilenameType)
  121.  
  122. class SchemaURL(SchemaSimpleItem):
  123.     def validate(self, data):
  124.         super(SchemaSimpleItem, self).validate(data)
  125.         self.validateType(data, unicode)
  126.         if data:
  127.             try:
  128.                 data.encode('ascii')
  129.             except UnicodeEncodeError:
  130.                 ValidationError(u"URL (%s) is not ASCII" % data)
  131.  
  132. class SchemaInt(SchemaSimpleItem):
  133.     def validate(self, data):
  134.         super(SchemaSimpleItem, self).validate(data)
  135.         self.validateTypes(data, [int, long])
  136.  
  137. class SchemaDateTime(SchemaSimpleItem):
  138.     def validate(self, data):
  139.         super(SchemaSimpleItem, self).validate(data)
  140.         self.validateType(data, datetime.datetime)
  141.  
  142. class SchemaTimeDelta(SchemaSimpleItem):
  143.     def validate(self, data):
  144.         super(SchemaSimpleItem, self).validate(data)
  145.         self.validateType(data, datetime.timedelta)
  146.  
  147. class SchemaList(SchemaItem):
  148.     def __init__(self, childSchema, noneOk=False):
  149.         super(SchemaList, self).__init__(noneOk)
  150.         self.childSchema = childSchema
  151.  
  152.     def validate(self, data):
  153.         super(SchemaList, self).validate(data)
  154.         self.validateType(data, list)
  155.  
  156. class SchemaDict(SchemaItem):
  157.     type = dict
  158.  
  159.     def __init__(self, keySchema, valueSchema, noneOk=False):
  160.         super(SchemaDict, self).__init__(noneOk)
  161.         self.keySchema = keySchema
  162.         self.valueSchema = valueSchema
  163.  
  164.     def validate(self, data):
  165.         super(SchemaDict, self).validate(data)
  166.         self.validateType(data, dict)
  167.  
  168. class SchemaSimpleContainer(SchemaSimpleItem):
  169.     """Allows nested dicts, lists and tuples, however the only thing they can
  170.     store are simple objects.  This currently includes bools, ints, longs,
  171.     floats, strings, unicode, None, datetime and struct_time objects.
  172.     """
  173.  
  174.     def validate(self, data):
  175.         super(SchemaSimpleContainer, self).validate(data)
  176.         self.validateTypes(data, (dict, list, tuple))
  177.         self.memory = set()
  178.         toValidate = LinkedList()
  179.         while data:
  180.             if id(data) in self.memory:
  181.                 return
  182.             else:
  183.                 self.memory.add(id(data))
  184.     
  185.             if isinstance(data, list) or isinstance(data, tuple):
  186.                 for item in data:
  187.                     toValidate.append(item)
  188.             elif isinstance(data, dict):
  189.                 for key, value in data.items():
  190.                     self.validateTypes(key, [bool, int, long, float, unicode,
  191.                         str, NoneType, datetime.datetime, time.struct_time])
  192.                     toValidate.append(value)
  193.             else:
  194.                 self.validateTypes(data, [bool, int, long, float, unicode,
  195.                         NoneType, datetime.datetime, time.struct_time])
  196.             try:
  197.                 data = toValidate.pop()
  198.             except:
  199.                 data = None
  200.  
  201. class SchemaStatusContainer(SchemaSimpleContainer):
  202.     """Allows nested dicts, lists and tuples, however the only thing they can
  203.     store are simple objects.  This currently includes bools, ints, longs,
  204.     floats, strings, unicode, None, datetime and struct_time objects.
  205.     """
  206.  
  207.     def validate(self, data):
  208.         from platformutils import FilenameType
  209.         if FilenameType == unicode:
  210.             binaryFields = ['metainfo','fastResumeData']
  211.         else:
  212.             binaryFields = ['channelName','shortFilename','filename','metainfo','fastResumeData']
  213.         self.validateType(data, dict)
  214.         for key, value in data.items():
  215.             self.validateTypes(key, [bool, int, long, float, unicode,
  216.                     str, NoneType, datetime.datetime, time.struct_time])
  217.             if key not in binaryFields:
  218.                 self.validateTypes(value, [bool, int, long, float, unicode,
  219.                         NoneType, datetime.datetime, time.struct_time])
  220.             else:
  221.                 self.validateType(value, str)
  222.  
  223. class SchemaObject(SchemaItem):
  224.     def __init__(self, klass, noneOk=False):
  225.         super(SchemaObject, self).__init__(noneOk)
  226.         self.klass = klass
  227.  
  228.     def validate(self, data):
  229.         super(SchemaObject, self).validate(data)
  230.         self.validateType(data, self.klass)
  231.  
  232. class ObjectSchema(object):
  233.     """The schema to save/restore an object with.  Object schema isn't a
  234.     SchemaItem, it's the schema for an entire object.
  235.  
  236.     Member variables:
  237.  
  238.     klass -- the python class that this schema is for
  239.     classString -- a human readable string that represents objectClass
  240.     fields -- list of  (name, SchemaItem) pairs.  One item for each attribute
  241.         that shoud be stored to disk.
  242.     """
  243.     pass
  244.  
  245. from database import DDBObject
  246. from downloader import RemoteDownloader, HTTPAuthPassword
  247. from feed import Feed, FeedImpl, RSSFeedImpl, ScraperFeedImpl
  248. from feed import SearchFeedImpl, DirectoryWatchFeedImpl, DirectoryFeedImpl, SearchDownloadsFeedImpl
  249. from feed import ManualFeedImpl, SingleFeedImpl
  250. from folder import ChannelFolder, PlaylistFolder
  251. from guide import ChannelGuide
  252. from item import Item, FileItem
  253. from iconcache import IconCache
  254. from playlist import SavedPlaylist
  255. from tabs import TabOrder
  256. from theme import ThemeHistory
  257.  
  258. class DDBObjectSchema(ObjectSchema):
  259.     klass = DDBObject
  260.     classString = 'ddb-object'
  261.     fields = [
  262.         ('id', SchemaInt())
  263.     ]
  264.  
  265. class IconCacheSchema (ObjectSchema):
  266.     klass = IconCache
  267.     classString = 'icon-cache'
  268.     fields = [
  269.         ('etag', SchemaString(noneOk=True)),
  270.         ('modified', SchemaString(noneOk=True)),
  271.         ('filename', SchemaFilename(noneOk=True)),
  272.         ('resized_filenames', SchemaDict(SchemaString(), SchemaFilename())),
  273.         ('url', SchemaURL(noneOk=True)),
  274.         ]
  275.  
  276. class ItemSchema(DDBObjectSchema):
  277.     klass = Item
  278.     classString = 'item'
  279.     fields = DDBObjectSchema.fields + [
  280.         ('feed_id', SchemaInt(noneOk=True)),
  281.         ('parent_id', SchemaInt(noneOk=True)),
  282.         ('seen', SchemaBool()),
  283.         ('autoDownloaded', SchemaBool()),
  284.         ('pendingManualDL', SchemaBool()),
  285.         ('pendingReason', SchemaString()),
  286.         ('entry', SchemaSimpleContainer()),
  287.         ('expired', SchemaBool()),
  288.         ('keep', SchemaBool()),
  289.         ('creationTime', SchemaDateTime()),
  290.         ('linkNumber', SchemaInt(noneOk=True)),
  291.         ('iconCache', SchemaObject(IconCache, noneOk=True)),
  292.         ('downloadedTime', SchemaDateTime(noneOk=True)),
  293.         ('watchedTime', SchemaDateTime(noneOk=True)),
  294.         ('isContainerItem', SchemaBool(noneOk=True)),
  295.         ('videoFilename', SchemaFilename()),
  296.         ('isVideo', SchemaBool()),
  297.         ('releaseDateObj', SchemaDateTime()),
  298.         ('eligibleForAutoDownload', SchemaBool()),
  299.         ('duration', SchemaInt(noneOk=True)),
  300.         ('screenshot', SchemaFilename(noneOk=True)),
  301.         ('resized_screenshots', SchemaDict(SchemaString(), SchemaFilename())),
  302.         ('resumeTime', SchemaInt()),
  303.     ]
  304.  
  305. class FileItemSchema(ItemSchema):
  306.     klass = FileItem
  307.     classString = 'file-item'
  308.     fields = ItemSchema.fields + [
  309.         ('filename', SchemaFilename()),
  310.         ('deleted', SchemaBool()),
  311.         ('shortFilename', SchemaFilename(noneOk=True)),
  312.         ('offsetPath', SchemaFilename(noneOk=True)),
  313.     ]
  314.  
  315. class FeedSchema(DDBObjectSchema):
  316.     klass = Feed
  317.     classString = 'feed'
  318.     fields = DDBObjectSchema.fields + [
  319.         ('origURL', SchemaURL()),
  320.         ('errorState', SchemaBool()),
  321.         ('loading', SchemaBool()),
  322.         ('actualFeed', SchemaObject(FeedImpl)),
  323.         ('iconCache', SchemaObject(IconCache, noneOk=True)),
  324.         ('folder_id', SchemaInt(noneOk=True)),
  325.         ('searchTerm', SchemaString(noneOk=True)),
  326.         ('userTitle', SchemaString(noneOk=True)),
  327.         ('autoDownloadable', SchemaBool()),
  328.         ('getEverything', SchemaBool()),
  329.         ('maxNew', SchemaInt()),
  330.         ('fallBehind', SchemaInt()),
  331.         ('expire', SchemaString()),
  332.         ('expireTime', SchemaTimeDelta(noneOk=True)),
  333.     ]
  334.  
  335. class FeedImplSchema(ObjectSchema):
  336.     klass = FeedImpl
  337.     classString = 'field-impl'
  338.     fields = [
  339.         ('url', SchemaURL()),
  340.         ('ufeed', SchemaObject(Feed)),
  341.         ('title', SchemaString()),
  342.         ('created', SchemaDateTime()),
  343.         ('visible', SchemaBool()),
  344.         ('lastViewed', SchemaDateTime()),
  345.         ('thumbURL', SchemaURL(noneOk=True)),
  346.         ('updateFreq', SchemaInt()),
  347.         ('initialUpdate', SchemaBool()),
  348.     ]
  349.  
  350. class RSSFeedImplSchema(FeedImplSchema):
  351.     klass = RSSFeedImpl
  352.     classString = 'rss-feed-impl'
  353.     fields = FeedImplSchema.fields + [
  354.         ('initialHTML', SchemaBinary(noneOk=True)),
  355.         ('etag', SchemaString(noneOk=True)),
  356.         ('modified', SchemaString(noneOk=True)),
  357.     ]
  358.  
  359. class ScraperFeedImplSchema(FeedImplSchema):
  360.     klass = ScraperFeedImpl
  361.     classString = 'scraper-feed-impl'
  362.     fields = FeedImplSchema.fields + [
  363.         ('initialHTML', SchemaBinary(noneOk=True)),
  364.         ('initialCharset', SchemaString(noneOk=True)),
  365.         ('linkHistory', SchemaSimpleContainer()),
  366.     ]
  367.  
  368. class SearchFeedImplSchema(FeedImplSchema):
  369.     klass = SearchFeedImpl
  370.     classString = 'search-feed-impl'
  371.     fields = FeedImplSchema.fields + [
  372.         ('searching', SchemaBool()),
  373.         ('lastEngine', SchemaString()),
  374.         ('lastQuery', SchemaString()),
  375.     ]
  376.  
  377. class DirectoryWatchFeedImplSchema(FeedImplSchema):
  378.     klass = DirectoryWatchFeedImpl
  379.     classString = 'directory-watch-feed-impl'
  380.     fields = FeedImplSchema.fields + [
  381.         ('firstUpdate', SchemaBool()),
  382.         ('dir', SchemaFilename(noneOk=True)),
  383.         ]
  384.  
  385. class DirectoryFeedImplSchema(FeedImplSchema):
  386.     klass = DirectoryFeedImpl
  387.     classString = 'directory-feed-impl'
  388.     # DirectoryFeedImpl doesn't have any addition fields over FeedImpl
  389.  
  390. class SearchDownloadsFeedImplSchema(FeedImplSchema):
  391.     klass = SearchDownloadsFeedImpl
  392.     classString = 'search-downloads-feed-impl'
  393.     # SearchDownloadsFeedImpl doesn't have any addition fields over FeedImpl
  394.  
  395. class ManualFeedImplSchema(FeedImplSchema):
  396.     klass = ManualFeedImpl
  397.     classString = 'manual-feed-impl'
  398.     # no addition fields over FeedImplSchema
  399.  
  400. class SingleFeedImplSchema(FeedImplSchema):
  401.     klass = SingleFeedImpl
  402.     classString = 'single-feed-impl'
  403.     # no addition fields over FeedImplSchema
  404.  
  405. class RemoteDownloaderSchema(DDBObjectSchema):
  406.     klass = RemoteDownloader
  407.     classString = 'remote-downloader'
  408.     fields = DDBObjectSchema.fields + [
  409.         ('url', SchemaURL()),
  410.         ('origURL', SchemaURL()),
  411.         ('dlid', SchemaString()),
  412.         ('contentType', SchemaString(noneOk=True)),
  413.         ('channelName', SchemaFilename(noneOk=True)),
  414.         ('status', SchemaStatusContainer()),
  415.         ('manualUpload', SchemaBool()),
  416.     ]
  417.  
  418. class HTTPAuthPasswordSchema(DDBObjectSchema):
  419.     klass = HTTPAuthPassword
  420.     classString = 'http-auth-password'
  421.     fields = DDBObjectSchema.fields + [
  422.         ('username', SchemaString()),
  423.         ('password', SchemaString()),
  424.         ('host', SchemaString()),
  425.         ('realm', SchemaString()),
  426.         ('path', SchemaString()),
  427.         ('authScheme', SchemaString()),
  428.     ]
  429.  
  430. class ChannelFolderSchema(DDBObjectSchema):
  431.     klass = ChannelFolder
  432.     classString = 'channel-folder'
  433.     fields = DDBObjectSchema.fields + [
  434.         ('expanded', SchemaBool()),
  435.         ('title', SchemaString()),
  436.     ]
  437.  
  438. class PlaylistFolderSchema(DDBObjectSchema):
  439.     klass = PlaylistFolder
  440.     classString = 'playlist-folder'
  441.     fields = DDBObjectSchema.fields + [
  442.         ('expanded', SchemaBool()),
  443.         ('title', SchemaString()),
  444.         ('item_ids', SchemaList(SchemaInt())),
  445.     ]
  446.  
  447. class PlaylistSchema(DDBObjectSchema):
  448.     klass = SavedPlaylist
  449.     classString = 'playlist'
  450.     fields = DDBObjectSchema.fields + [
  451.         ('title', SchemaString()),
  452.         ('item_ids', SchemaList(SchemaInt())),
  453.         ('folder_id', SchemaInt(noneOk=True)),
  454.     ]
  455.  
  456. class TabOrderSchema(DDBObjectSchema):
  457.     klass = TabOrder
  458.     classString = 'taborder-order'
  459.     fields = DDBObjectSchema.fields + [
  460.         ('type', SchemaString()),
  461.         ('tab_ids', SchemaList(SchemaInt())),
  462.     ]
  463.  
  464. class ChannelGuideSchema(DDBObjectSchema):
  465.     klass = ChannelGuide
  466.     classString = 'channel-guide'
  467.     fields = DDBObjectSchema.fields + [
  468.         ('url', SchemaURL(noneOk=True)),
  469.         ('updated_url', SchemaURL(noneOk=True)),
  470.         ('favicon', SchemaURL(noneOk=True)),
  471.         ('title', SchemaString(noneOk=True)),
  472.         ('iconCache', SchemaObject(IconCache, noneOk=True)),
  473.         ('firstTime', SchemaBool()),
  474.     ]
  475.  
  476. class ThemeHistorySchema(DDBObjectSchema):
  477.     klass = ThemeHistory
  478.     classString = 'theme-history'
  479.     fields = DDBObjectSchema.fields + [
  480.         ('lastTheme', SchemaString(noneOk=True)),
  481.         ('pastThemes', SchemaList(SchemaString(noneOk=False), noneOk=False)),
  482.     ]
  483.  
  484. VERSION = 57
  485. objectSchemas = [ 
  486.     DDBObjectSchema, IconCacheSchema, ItemSchema, FileItemSchema, FeedSchema,
  487.     FeedImplSchema, RSSFeedImplSchema, ScraperFeedImplSchema,
  488.     SearchFeedImplSchema, DirectoryFeedImplSchema, DirectoryWatchFeedImplSchema,
  489.     SearchDownloadsFeedImplSchema, RemoteDownloaderSchema,
  490.     HTTPAuthPasswordSchema, ChannelGuideSchema, ManualFeedImplSchema, SingleFeedImplSchema,
  491.     PlaylistSchema, ChannelFolderSchema, PlaylistFolderSchema,
  492.     TabOrderSchema, ThemeHistorySchema
  493. ]
  494.