File: feed.py

package info (click to toggle)
python-feedvalidator 0~svn1022-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd, squeeze, wheezy
  • size: 652 kB
  • ctags: 2,452
  • sloc: python: 9,481; makefile: 27; sh: 8
file content (168 lines) | stat: -rw-r--r-- 5,427 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
"""$Id: feed.py 988 2008-03-12 18:22:48Z sa3ruby $"""

__author__ = "Sam Ruby <http://intertwingly.net/> and Mark Pilgrim <http://diveintomark.org/>"
__version__ = "$Revision: 988 $"
__copyright__ = "Copyright (c) 2002 Sam Ruby and Mark Pilgrim"

from base import validatorBase
from validators import *
from logging import *
from itunes import itunes_channel
from extension import extension_feed

#
# Atom root element
#
class feed(validatorBase, extension_feed, itunes_channel):
  def getExpectedAttrNames(self):
    return [(u'urn:atom-extension:indexing', u'index')]

  def prevalidate(self):
    self.links = []
    self.validate_optional_attribute((u'urn:atom-extension:indexing', u'index'), yesno)
    
  def missingElement(self, params):
    offset = [self.line - self.dispatcher.locator.getLineNumber(),
              self.col  - self.dispatcher.locator.getColumnNumber()]
    self.log(MissingElement(params), offset)

  def validate_metadata(self):
    if not 'title' in self.children:
      self.missingElement({"parent":self.name, "element":"title"})
    if not 'id' in self.children:
      self.missingElement({"parent":self.name, "element":"id"})
    if not 'updated' in self.children:
      self.missingElement({"parent":self.name, "element":"updated"})

    # complete feeds can only have current=self and no other links
    if 'fh_complete' in self.children:
      for link in self.links:
        if link.rel in link.rfc5005:
          if link.rel == "current":
            if link.href not in self.dispatcher.selfURIs:
              self.log(CurrentNotSelfInCompleteFeed({"rel":link.rel}))
          else:
            self.log(FeedRelInCompleteFeed({"rel":link.rel}))

    # ensure that there is a link rel="self"
    if self.name != 'source':
      for link in self.links:
        if link.rel=='self': break
      else:
        offset = [self.line - self.dispatcher.locator.getLineNumber(),
                  self.col  - self.dispatcher.locator.getColumnNumber()]
        self.log(MissingSelf({"parent":self.parent.name, "element":self.name}), offset)

    types={}
    archive=False
    current=False
    for link in self.links:
      if link.rel == 'current': current = True
      if link.rel in ['prev-archive', 'next-archive']: archive = True

      # attempts to link past the end of the list
      if link.rel == 'first' and link.href in self.dispatcher.selfURIs:
        for link2 in self.links:
          if link2.rel == 'previous':
              self.log(LinkPastEnd({"self":link.rel, "rel":link2.rel}))
      if link.rel == 'last' and link.href in self.dispatcher.selfURIs:
        for link2 in self.links:
          if link2.rel == 'next':
              self.log(LinkPastEnd({"self":link.rel, "rel":link2.rel}))

      # can only have one alternate per type
      if not link.rel=='alternate': continue
      if not link.type in types: types[link.type]={}
      if link.rel in types[link.type]:
        if link.hreflang in types[link.type][link.rel]:
          self.log(DuplicateAtomLink({"parent":self.name, "element":"link", "type":link.type, "hreflang":link.hreflang}))
        else:
          types[link.type][link.rel] += [link.hreflang]
      else:
        types[link.type][link.rel] = [link.hreflang]

    if 'fh_archive' in self.children:
      # archives should either have links or be marked complete
      if not archive and 'fh_complete' not in self.children:
        self.log(ArchiveIncomplete({}))

      # archives should have current links
      if not current and ('fh_complete' not in self.children):
        self.log(MissingCurrentInArchive({}))
 
    if self.itunes: itunes_channel.validate(self)

  def metadata(self):
    if 'entry' in self.children:
      self.log(MisplacedMetadata({"parent":self.name, "element":self.child}))

  def validate(self):
    if not 'entry' in self.children:
      self.validate_metadata()

  def do_author(self):
    self.metadata()
    from author import author
    return author()

  def do_category(self):
    self.metadata()
    from category import category
    return category()

  def do_contributor(self):
    self.metadata()
    from author import author
    return author()

  def do_generator(self):
    self.metadata()
    from generator import generator
    return generator(), nonblank(), noduplicates()

  def do_id(self):
    self.metadata()
    return canonicaluri(), nows(), noduplicates()

  def do_icon(self):
    self.metadata()
    return nonblank(), nows(), rfc2396(), noduplicates()

  def do_link(self):
    self.metadata()
    from link import link
    self.links.append(link())
    return self.links[-1]

  def do_logo(self):
    self.metadata()
    return nonblank(), nows(), rfc2396(), noduplicates()

  def do_title(self):
    self.metadata()
    from content import textConstruct
    return textConstruct(), noduplicates()
  
  def do_subtitle(self):
    self.metadata()
    from content import textConstruct
    return textConstruct(), noduplicates()
  
  def do_rights(self):
    self.metadata()
    from content import textConstruct
    return textConstruct(), noduplicates()

  def do_updated(self):
    self.metadata()
    return rfc3339(), nows(), noduplicates()

  def do_entry(self):
    if not 'entry' in self.children:
      self.validate_metadata()
    from entry import entry
    return entry()

  def do_app_collection(self):
    from service import collection
    return collection(), noduplicates()