1
1
Adding new tags
2
2
===============
3
3
4
- Are there XML tags you want to use that aren't supported by PodGen? If so, you
5
- should be able to add them in using inheritance.
4
+ Are there XML elements you want to use that aren't supported by PodGen? If so,
5
+ you should be able to add them in using inheritance.
6
+
7
+ .. warning ::
8
+
9
+ This is an advanced topic.
6
10
7
11
.. note ::
8
12
@@ -11,47 +15,67 @@ should be able to add them in using inheritance.
11
15
12
16
.. note ::
13
17
14
- Feel free to add a feature request to GitHub Issues if you think PodGen
15
- should support a certain tag out of the box.
18
+ Feel free to add a feature request to `GitHub Issues `_ if you think PodGen
19
+ should support a certain element out of the box.
20
+
21
+ .. _GitHub Issues : https://github.com/tobinus/python-podgen/issues
16
22
17
23
18
24
Quick How-to
19
25
------------
20
26
21
- #. Create new class that extends Podcast.
27
+ #. Create new class that extends :class: ` . Podcast` .
22
28
#. Add the new attribute.
23
- #. Override :meth: `.Podcast._create_rss `, call super()._create_rss(),
24
- add the new tag to its result and return the new tree.
29
+ #. Override :meth: `~.Podcast._create_rss `, call ``super()._create_rss() ``,
30
+ add the new element to its result and return the new tree.
31
+
32
+ You can do the same with :class: `.Episode `, if you replace
33
+ :meth: `~.Podcast._create_rss ` with :meth: `~Episode.rss_entry ` above.
34
+
35
+ There are plenty of small quirks you have to keep in mind. You are strongly
36
+ encouraged to read the example below.
37
+
38
+ Using namespaces
39
+ ^^^^^^^^^^^^^^^^
25
40
26
41
If you'll use RSS elements from another namespace, you must make sure you
27
- update the _nsmap attribute of Podcast (you cannot define new namespaces from
28
- an episode!). It is a dictionary with the prefix as key and the
29
- URI for that namespace as value. To use a namespace, you must put the URI inside
30
- curly braces, with the tag name following right after (outside the braces).
31
- For example::
42
+ update the :attr: ` ~.Podcast. _nsmap` attribute of :class: ` . Podcast`
43
+ (you cannot define new namespaces from an episode!). It is a dictionary with the
44
+ prefix as key and the URI for that namespace as value. To use a namespace, you
45
+ must put the URI inside curly braces, with the tag name following right after
46
+ (outside the braces). For example::
32
47
33
48
"{%s}link" % self._nsmap['atom'] # This will render as atom:link
34
49
35
- The `lxml API documentation `_ is a pain to read, so just look at the source code
36
- for PodGen to figure out how to do things. The example below may help, too .
50
+ The `lxml API documentation `_ is a pain to read, so just look at the ` source code
51
+ for PodGen `_ and the example below.
37
52
38
53
.. _lxml API documentation : http://lxml.de/api/index.html
54
+ .. _source code for PodGen : https://github.com/tobinus/python-podgen/blob/master/podgen/podcast.py
39
55
40
- You can do the same with Episode, if you replace _create_rss() with
41
- rss_entry() above.
42
-
43
- Example: Adding a ttl field
44
- ---------------------------
56
+ Example: Adding a ttl element
57
+ -----------------------------
45
58
46
59
The examples here assume version 3 of Python is used.
47
60
61
+ ``ttl `` is an RSS element and stands for "time to live", and can only be an
62
+ integer which indicates how many minutes the podcatcher can rely on its copy of
63
+ the feed before refreshing (or something like that). There is confusion as to
64
+ what it is supposed to mean (max refresh frequency? min refresh frequency?),
65
+ which is why it is not included in PodGen. If you use it, you should treat it as
66
+ the **recommended ** update period (source: `RSS Best Practices `_).
67
+
68
+ .. _RSS Best Practices : http://www.rssboard.org/rss-profile#element-channel-ttl
69
+
48
70
Using traditional inheritance
49
71
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50
72
51
73
::
52
74
75
+ # The module used to create the XML tree and generate the XML
53
76
from lxml import etree
54
77
78
+ # The class we will extend
55
79
from podgen import Podcast
56
80
57
81
@@ -73,6 +97,9 @@ Using traditional inheritance
73
97
# Call Podcast's constructor
74
98
super().__init__(*args, **kwargs)
75
99
100
+ # If we were to use another namespace, we would add this here:
101
+ # self._nsmap['prefix'] = "URI"
102
+
76
103
@property
77
104
def ttl(self):
78
105
"""Your suggestion for how many minutes podcatchers should wait
@@ -83,42 +110,59 @@ Using traditional inheritance
83
110
:type: :obj:`int`
84
111
:RSS: ttl
85
112
"""
113
+ # By using @property and @ttl.setter, we encapsulate the ttl field
114
+ # so that we can check the value that is assigned to it.
115
+ # If you don't need this, you could just rename self.__ttl to
116
+ # self.ttl and remove those two methods.
86
117
return self.__ttl
87
118
88
119
@ttl.setter
89
120
def ttl(self, ttl):
121
+ # Try to convert to int
90
122
try:
91
123
ttl_int = int(ttl)
92
124
except ValueError:
93
125
raise TypeError("ttl expects an integer, got %s" % ttl)
94
-
126
+ # Is this negative?
95
127
if ttl_int < 0:
96
128
raise ValueError("Negative ttl values aren't accepted, got %s"
97
129
% ttl_int)
130
+ # All checks passed
98
131
self.__ttl = ttl_int
99
132
100
133
def _create_rss(self):
134
+ # Let Podcast generate the lxml etree (adding the standard elements)
101
135
rss = super()._create_rss()
136
+ # We must get the channel element, since we want to add subelements
137
+ # to it.
102
138
channel = rss.find("channel")
139
+ # Only add the ttl element if it has been populated.
103
140
if self.__ttl is not None:
141
+ # First create our new subelement of channel.
104
142
ttl = etree.SubElement(channel, 'ttl')
143
+ # If we were to use another namespace, we would instead do this:
144
+ # ttl = etree.SubElement(channel,
145
+ # '{%s}ttl' % self._nsmap['prefix'])
146
+
147
+ # Then, fill it with the ttl value
105
148
ttl.text = str(self.__ttl)
106
149
150
+ # Return the new etree, now with ttl
107
151
return rss
108
152
109
153
# How to use the new class (normally, you would put this somewhere else)
110
154
myPodcast = PodcastWithTtl(name="Test", website="http://example.org",
111
155
explicit=False, description="Testing ttl")
112
- myPodcast.ttl = 90
156
+ myPodcast.ttl = 90 # or set ttl=90 in the constructor
113
157
print(myPodcast)
114
158
115
159
116
160
Using mixins
117
161
^^^^^^^^^^^^
118
162
119
- To use mixins, you cannot make the class with the ttl functionality inherit
120
- Podcast. Instead, it must inherit nothing. Other than that, the code will be
121
- the same, so it doesn't make sense to repeat it here.
163
+ To use mixins, you cannot make the class with the `` ttl `` functionality inherit
164
+ :class: ` . Podcast` . Instead, it must inherit nothing. Other than that, the code
165
+ will be the same, so it doesn't make sense to repeat it here.
122
166
123
167
::
124
168
@@ -137,28 +181,29 @@ the same, so it doesn't make sense to repeat it here.
137
181
138
182
Note the order of the mixins in the class declaration. You should read it as
139
183
the path Python takes when looking for a method. First Python checks
140
- PodcastWithTtl, then TtlMixin, finally Podcast. This is also the order the
141
- methods are called when chained together using super(). If you had Podcast
142
- first, Podcast's _create_rss() method would be run first, and since it never
143
- calls super()._create_rss(), the TtlMixin's _create_rss would never be run.
144
- Therefore, you should always have Podcast last in that list.
184
+ ``PodcastWithTtl ``, then ``TtlMixin `` and finally :class: `.Podcast `. This is
185
+ also the order the methods are called when chained together using :func: `super `.
186
+ If you had Podcast first, :meth: `.Podcast._create_rss ` method would be run
187
+ first, and since it never calls ``super()._create_rss() ``, the ``TtlMixin ``'s
188
+ ``_create_rss `` would never be run. Therefore, you should always have
189
+ :class: `.Podcast ` last in that list.
145
190
146
191
Which approach is best?
147
192
^^^^^^^^^^^^^^^^^^^^^^^
148
193
149
194
The advantage of mixins isn't really displayed here, but it will become
150
195
apparent as you add more and more extensions. Say you define 5 different mixins,
151
- which all add exactly one more attribute to Podcast. If you used traditional
196
+ which all add exactly one more element to :class: ` . Podcast` . If you used traditional
152
197
inheritance, you would have to make sure each of those 5 subclasses made up a
153
- tree. That is, class 1 would inherit Podcast. Class 2 would have to inherit
198
+ tree. That is, class 1 would inherit :class: ` . Podcast` . Class 2 would have to inherit
154
199
class 1, class 3 would have to inherit class 2 and so on. If two of the classes
155
- had the same superclass, you would be screwed.
200
+ had the same superclass, you could get screwed.
156
201
157
202
By using mixins, you can put them together however you want. Perhaps for one
158
- podcast you only need ttl, while for another podcast you want to use the
159
- textInput element in addition to ttl, and another podcast requires the
160
- textInput element together with the comments element. Using traditional
161
- inheritance, you would have to duplicate code for textInput in two classes. Not
203
+ podcast you only need `` ttl `` , while for another podcast you want to use the
204
+ `` textInput `` element in addition to `` ttl `` , and another podcast requires the
205
+ `` textInput `` element together with the `` comments `` element. Using traditional
206
+ inheritance, you would have to duplicate code for `` textInput `` in two classes. Not
162
207
so with mixins::
163
208
164
209
class PodcastWithTtl(TtlMixin, Podcast):
@@ -174,6 +219,6 @@ so with mixins::
174
219
def __init__(*args, **kwargs):
175
220
super().__init__(*args, **kwargs)
176
221
177
- If the list of attributes you want to use varies between different podcasts,
222
+ If the list of elements you want to use varies between different podcasts,
178
223
mixins are the way to go. On the other hand, mixins are overkill if you are okay
179
- with one giant class with all the attributes you need.
224
+ with one giant class with all the elements you need.
0 commit comments