Merge lp:~jml/pkgme/copyright into lp:pkgme

Proposed by Jonathan Lange
Status: Merged
Approved by: James Westby
Approved revision: 134
Merged at revision: 130
Proposed branch: lp:~jml/pkgme/copyright
Merge into: lp:pkgme
Diff against target: 372 lines (+148/-43)
5 files modified
pkgme/info_elements.py (+60/-28)
pkgme/package_files.py (+2/-0)
pkgme/templates/copyright (+4/-0)
pkgme/tests/test_info_elements.py (+32/-13)
pkgme/tests/test_package_files.py (+50/-2)
To merge this branch: bzr merge lp:~jml/pkgme/copyright
Reviewer Review Type Date Requested Status
James Westby Approve
Review via email: mp+120594@code.launchpad.net

Commit message

Add ExplicitCopyright element for overriding default copyright.

Description of the change

As per https://bugs.launchpad.net/pkgme-devportal/+bug/1026121 we want to
be able to override the default copyright ('$year $maintainer') with custom
text from certain backends.

This branch enables that by adding an ExplicitCopyright info_element and using
that instead of $year $maintainer if it's set.

According to http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/#file-syntax
the copyright field must be formatted the same way as the description field
in the control file. Thus, I extracted the debian formatting bit from description,
re-targetted the description tests against that & re-used it for copyright.

To post a comment you must log in.
Revision history for this message
James Westby (james-w) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'pkgme/info_elements.py'
2--- pkgme/info_elements.py 2012-07-18 08:51:10 +0000
3+++ pkgme/info_elements.py 2012-08-21 15:30:25 +0000
4@@ -217,11 +217,58 @@
5
6 Named after the Perl built-in that does the same thing.
7 """
8+ # XXX: This is unused in pkgme, can we delete it? -- jml
9 if text and text[-1] == '\n':
10 return text[:-1]
11 return text
12
13
14+
15+def _format_lines(lines):
16+ first_line, lines = lines[0], lines[1:]
17+ yield first_line
18+ for i, line in enumerate(lines):
19+ is_blank = bool(not line.strip())
20+ if is_blank:
21+ yield ' .'
22+ elif line[0] == ' ':
23+ yield line
24+ else:
25+ yield ' ' + line
26+
27+
28+def debian_formatted_text(user_text):
29+ """Prepare ``user_text`` as Debian formatted text.
30+
31+ If ``user_text`` looks to already be formatted according to Debian policy,
32+ then we pass it through. If it does not, then we do our best to clean it
33+ up.
34+
35+ We determine whether ``user_text`` is already Debian-formatted by checking
36+ whether the second line is indented by a space. If so, then we assume it's
37+ Debian-formatted. If later lines break Debian formatting, then we raise a
38+ ValueError.
39+
40+ This style of formatting is used for the Description field of the
41+ ``debian/control`` file, and for various fields in the
42+ ``debian/copyright`` file.
43+
44+ See also:
45+ * http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/#file-syntax
46+ * http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description
47+
48+ :param user_text: A string to be formatted as text for Debian files.
49+ :raise ValueError: When the string initially looks to be Debian-formatted
50+ but actually is not.
51+ :return: A correctly formatted version of that string.
52+ """
53+ if isinstance(user_text, unicode):
54+ user_text = user_text.encode('utf-8')
55+ if not user_text.strip():
56+ return ''
57+ return '\n'.join(_format_lines(user_text.strip().splitlines()))
58+
59+
60 class Description(InfoElement):
61
62 name = "description"
63@@ -229,31 +276,9 @@
64 default = "a package"
65
66 @classmethod
67- def _format_lines(cls, lines):
68- first_line, lines = lines[0], lines[1:]
69- yield first_line
70- for i, line in enumerate(lines):
71- is_blank = bool(not line.strip())
72- if is_blank:
73- yield ' .'
74- elif line[0] == ' ':
75- yield line
76- else:
77- yield ' ' + line
78-
79- @classmethod
80 def clean(cls, value):
81 """Prepare a string for the Description field in the control file.
82
83- If ``value`` looks to already be formatted according to Debian policy,
84- then we pass it through. If it does not, then we do our best to clean
85- it up.
86-
87- We determine whether ``value`` is already Debian-formatted by checking
88- whether the second line is indented by a space. If so, then we assume
89- it's Debian-formatted. If later lines break Debian formatting, then
90- we raise a ValueError.
91-
92 See http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description
93 for the rules governing the Description field.
94
95@@ -261,13 +286,9 @@
96 file.
97 :raise ValueError: When the string initially looks to be Debian-
98 formatted but actually is not.
99- :return: A correctly-formatted version of that string.
100+ :return: A correctly formatted version of that string.
101 """
102- if isinstance(value, unicode):
103- value = value.encode('utf-8')
104- if not value.strip():
105- return ''
106- return '\n'.join(cls._format_lines(value.strip().splitlines()))
107+ return debian_formatted_text(value)
108
109
110 class Version(InfoElement):
111@@ -328,6 +349,17 @@
112 description = "The path to the executable for an application."
113
114
115+class ExplicitCopyright(InfoElement):
116+
117+ name = 'explicit_copyright'
118+ description = 'An explicit copyright statement to override the default.'
119+
120+ @classmethod
121+ def clean(cls, value):
122+ if value is not None:
123+ return debian_formatted_text(value)
124+
125+
126 class TagLine(InfoElement):
127
128 name = 'tagline'
129
130=== modified file 'pkgme/package_files.py'
131--- pkgme/package_files.py 2012-07-20 19:24:04 +0000
132+++ pkgme/package_files.py 2012-08-21 15:30:25 +0000
133@@ -17,6 +17,7 @@
134 Description,
135 Distribution,
136 Executable,
137+ ExplicitCopyright,
138 ExtraFiles,
139 ExtraFilesFromPaths,
140 ExtraTargets,
141@@ -181,6 +182,7 @@
142 # See http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
143
144 elements = [
145+ ExplicitCopyright,
146 PackageName,
147 License,
148 Homepage,
149
150=== modified file 'pkgme/templates/copyright'
151--- pkgme/templates/copyright 2012-07-05 14:16:12 +0000
152+++ pkgme/templates/copyright 2012-08-21 15:30:25 +0000
153@@ -5,7 +5,11 @@
154 Upstream-Contact: $maintainer
155
156 Files: *
157+#if $explicit_copyright
158+Copyright: $explicit_copyright
159+#else
160 Copyright: $year $maintainer
161+#end if
162 License: $license
163 #import os.path
164 #set $path = os.path.join("/usr/share/common-licenses", $license)
165
166=== modified file 'pkgme/tests/test_info_elements.py'
167--- pkgme/tests/test_info_elements.py 2012-06-04 16:40:24 +0000
168+++ pkgme/tests/test_info_elements.py 2012-08-21 15:30:25 +0000
169@@ -5,6 +5,7 @@
170
171 from pkgme.info_elements import (
172 BuildDepends,
173+ debian_formatted_text,
174 Description,
175 Distribution,
176 ExtraFiles,
177@@ -153,7 +154,7 @@
178 Distribution.get_default(), release_keys['DISTRIB_CODENAME'])
179
180
181-class DescriptionTestCase(TestCase):
182+class DebianFormattedTextTests(TestCase):
183
184 # http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description
185
186@@ -161,14 +162,14 @@
187 # If the description is just a single line with no line breaks, then
188 # that counts as the synopsis and no special formatting is needed.
189 description = "single line description"
190- cleaned = Description.clean(description)
191+ cleaned = debian_formatted_text(description)
192 self.assertEqual(description, cleaned)
193
194 def test_single_line_trailing_newline(self):
195 # If the single line ends with a newline then we remove that newline,
196 # as it's not needed for the Description in the template.
197 description = "single line description\n"
198- cleaned = Description.clean(description)
199+ cleaned = debian_formatted_text(description)
200 self.assertEqual(description.rstrip(), cleaned)
201
202 def test_indented_further_lines(self):
203@@ -180,7 +181,7 @@
204 more preformatted text
205 more information
206 """
207- cleaned = Description.clean(description)
208+ cleaned = debian_formatted_text(description)
209 self.assertEqual(description.rstrip(), cleaned)
210
211 def test_non_indented_further_lines(self):
212@@ -193,7 +194,7 @@
213 follow-up line
214 more information
215 """
216- description = Description.clean(bad_description)
217+ description = debian_formatted_text(bad_description)
218 self.assertEqual("""\
219 initial synopsis
220 follow-up line
221@@ -209,7 +210,7 @@
222
223 more information
224 """
225- description = Description.clean(bad_description)
226+ description = debian_formatted_text(bad_description)
227 self.assertEqual("""initial synopsis
228 follow-up line
229 .
230@@ -223,7 +224,7 @@
231 follow-up line
232 more information
233 """
234- cleaned = Description.clean(description)
235+ cleaned = debian_formatted_text(description)
236 self.assertEqual("""initial synopsis
237 follow-up line
238 more information""", cleaned)
239@@ -239,7 +240,7 @@
240 that previous line has spaces in it, but needs to be marked
241 up as a blank line all the same
242 """
243- cleaned = Description.clean(description)
244+ cleaned = debian_formatted_text(description)
245 self.assertEqual("""initial synopsis
246 follow-up line
247 .
248@@ -250,9 +251,9 @@
249
250 def test_empty_description(self):
251 # If the description is empty, then we return an empty description.
252- self.assertEqual('', Description.clean(''))
253- self.assertEqual('', Description.clean('\n'))
254- self.assertEqual('', Description.clean(' '))
255+ self.assertEqual('', debian_formatted_text(''))
256+ self.assertEqual('', debian_formatted_text('\n'))
257+ self.assertEqual('', debian_formatted_text(' '))
258
259 def test_initial_blank_lines(self):
260 description = """
261@@ -261,7 +262,7 @@
262 follow-up line
263 more information
264 """
265- cleaned = Description.clean(description)
266+ cleaned = debian_formatted_text(description)
267 self.assertEqual("""initial synopsis
268 follow-up line
269 more information""", cleaned)
270@@ -272,10 +273,28 @@
271 description = (
272 u'\u201cPretty \u2018speech\u2019 marks\u201d\u202a \u2013'
273 u'what fun!')
274- cleaned = Description.clean(description)
275+ cleaned = debian_formatted_text(description)
276 self.assertEqual(description.encode('utf-8'), cleaned)
277
278
279+class DescriptionTestCase(TestCase):
280+
281+ # http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description
282+
283+ def test_debian_formats_text(self):
284+ # Description.clean returns the description as Debian-formatted text.
285+ description = """initial synopsis
286+follow-up line
287+
288+more information
289+
290+that previous line has spaces in it, but needs to be marked
291+up as a blank line all the same
292+"""
293+ cleaned = Description.clean(description)
294+ self.assertEqual(debian_formatted_text(description), cleaned)
295+
296+
297 class TestMaintainer(TestCase):
298
299 def test_unspecified(self):
300
301=== modified file 'pkgme/tests/test_package_files.py'
302--- pkgme/tests/test_package_files.py 2012-07-20 19:24:04 +0000
303+++ pkgme/tests/test_package_files.py 2012-08-21 15:30:25 +0000
304@@ -13,9 +13,11 @@
305 Buildsystem,
306 Categories,
307 DebhelperAddons,
308+ debian_formatted_text,
309 Depends,
310 Description,
311 Executable,
312+ ExplicitCopyright,
313 ExtraFiles,
314 ExtraFilesFromPaths,
315 ExtraTargets,
316@@ -260,8 +262,54 @@
317 Files: debian/*
318 Copyright: %(year)s The friendly pkgme.net robot
319 License: public-domain
320-""" % { 'year' : datetime.datetime.now().year,
321- })
322+""" % {'year' : datetime.datetime.now().year,})
323+
324+ def test_explicit_copyright(self):
325+ package_file = self.get_object(
326+ {ExplicitCopyright.name: "blah blah blah",
327+ License.name: 'proprietary',
328+ })
329+ self.assertEqual(package_file.get_contents(),
330+ """\
331+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
332+Upstream-Name: somepackage
333+Upstream-Contact: Random Guy <foo@example.com>
334+
335+Files: *
336+Copyright: blah blah blah
337+License: proprietary
338+
339+Files: debian/*
340+Copyright: %(year)s The friendly pkgme.net robot
341+License: public-domain
342+""" % {'year' : datetime.datetime.now().year,})
343+
344+ def test_explicit_copyright_multiline(self):
345+ copyright_msg = """\
346+Please see the enclosed PDF file for the exact copyright holders or contact the submitter of the application '%(submitter)s'
347+
348+This file was automatically generated.
349+""" % {'submitter': 'Arthur Dent <arthur.dent@canonical.com'}
350+ package_file = self.get_object(
351+ {ExplicitCopyright.name: copyright_msg,
352+ License.name: 'proprietary',
353+ })
354+ self.assertEqual(package_file.get_contents(),
355+ """\
356+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
357+Upstream-Name: somepackage
358+Upstream-Contact: Random Guy <foo@example.com>
359+
360+Files: *
361+Copyright: %(clean_copyright)s
362+License: proprietary
363+
364+Files: debian/*
365+Copyright: %(year)s The friendly pkgme.net robot
366+License: public-domain
367+""" % {'year' : datetime.datetime.now().year,
368+ 'clean_copyright': debian_formatted_text(copyright_msg),
369+ })
370
371 def test_overwrite(self):
372 package_file = self.get_object()

Subscribers

People subscribed via source and target branches