Hello community,
here is the log from the commit of package python-podcastparser for
openSUSE:Factory checked in at 2020-07-16 12:17:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-podcastparser (Old)
and /work/SRC/openSUSE:Factory/.python-podcastparser.new.3592 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-podcastparser"
Thu Jul 16 12:17:36 2020 rev:4 rq:821147 version:0.6.5
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-podcastparser/python-podcastparser.changes
2019-06-03 18:55:30.000425969 +0200
+++
/work/SRC/openSUSE:Factory/.python-podcastparser.new.3592/python-podcastparser.changes
2020-07-16 12:20:24.315083194 +0200
@@ -1,0 +2,9 @@
+Wed Jul 15 14:36:39 UTC 2020 - [email protected]
+
+- version update to 0.6.5
+ * no upstream changelog
+- added patches
+ fix https://github.com/gpodder/podcastparser/pull/21
+ + python-podcastparser-remove-nose.patch
+
+-------------------------------------------------------------------
Old:
----
podcastparser-0.6.4.tar.gz
New:
----
podcastparser-0.6.5.tar.gz
python-podcastparser-remove-nose.patch
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-podcastparser.spec ++++++
--- /var/tmp/diff_new_pack.jJ9Bng/_old 2020-07-16 12:20:26.955085864 +0200
+++ /var/tmp/diff_new_pack.jJ9Bng/_new 2020-07-16 12:20:26.955085864 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-podcastparser
#
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,14 +18,18 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-podcastparser
-Version: 0.6.4
+Version: 0.6.5
Release: 0
Summary: A podcast parser
License: ISC
Group: Development/Libraries/Python
URL: https://github.com/gpodder/podcastparser
Source:
https://files.pythonhosted.org/packages/source/p/podcastparser/podcastparser-%{version}.tar.gz
+# https://github.com/gpodder/podcastparser/pull/21
+Patch0: python-podcastparser-remove-nose.patch
BuildRequires: %{python_module nose}
+BuildRequires: %{python_module pytest-cov}
+BuildRequires: %{python_module pytest}
BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module xml}
BuildRequires: fdupes
@@ -39,6 +43,7 @@
%prep
%setup -q -n podcastparser-%{version}
+%patch0 -p1
%build
%python_build
@@ -48,7 +53,7 @@
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
-%python_expand nosetests-%{$python_bin_suffix}
+%pytest
%files %{python_files}
%license LICENSE
++++++ podcastparser-0.6.4.tar.gz -> podcastparser-0.6.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/podcastparser-0.6.4/LICENSE
new/podcastparser-0.6.5/LICENSE
--- old/podcastparser-0.6.4/LICENSE 2018-08-19 18:16:16.000000000 +0200
+++ new/podcastparser-0.6.5/LICENSE 2020-04-07 22:01:05.000000000 +0200
@@ -1,5 +1,7 @@
-Copyright (c) 2012, 2013, 2014, 2018, Thomas Perl <[email protected]>
+Copyright (c) 2012, 2013, 2014, 2018, 2020 Thomas Perl <[email protected]>
+Copyright (c) 2016, 2017, 2018, 2019, 2020 Eric Le Lay <[email protected]>
+Copyright (c) 2020 E.S. Rosenberg <[email protected]>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/podcastparser-0.6.4/PKG-INFO
new/podcastparser-0.6.5/PKG-INFO
--- old/podcastparser-0.6.4/PKG-INFO 2018-08-19 18:16:34.000000000 +0200
+++ new/podcastparser-0.6.5/PKG-INFO 2020-04-07 22:02:14.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: podcastparser
-Version: 0.6.4
+Version: 0.6.5
Summary: Simplified, fast RSS parser
Home-page: http://gpodder.org/podcastparser/
Author: Thomas Perl
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/podcastparser-0.6.4/doc/index.rst
new/podcastparser-0.6.5/doc/index.rst
--- old/podcastparser-0.6.4/doc/index.rst 2018-08-19 18:16:16.000000000
+0200
+++ new/podcastparser-0.6.5/doc/index.rst 2020-04-07 21:57:03.000000000
+0200
@@ -102,6 +102,12 @@
Episode website.
**rss/channel/item/description**
+ Episode description.
+ If it contains html, it's returned as description_html.
+ Otherwise it's returned as description (whitespace is squashed).
+ See Mozilla's article `Why RSS Content Module is Popular`
+
+**rss/channel/item/itunes:summary**
Episode description (whitespace is squashed).
**rss/channel/item/itunes:subtitle**
@@ -109,6 +115,7 @@
**rss/channel/item/content:encoded**
Episode description in HTML.
+ Best source for description_html.
**rss/channel/item/itunes:duration**
Episode duration.
@@ -122,6 +129,9 @@
**rss/channel/item/atom:link@rel=enclosure**
File download URL (@href), size (@length) and mime type (@type).
+**rss/channel/item/itunes:image**
+ Episode art URL.
+
**rss/channel/item/media:content**
File download URL (@url), size (@fileSize) and mime type (@type).
@@ -134,6 +144,7 @@
**rss/channel/item/psc:chapters/psc:chapter**
Chapter entry (@start, @title, @href and @image).
+.. _Why RSS Content Module is Popular:
https://developer.mozilla.org/en-US/docs/Web/RSS/Article/Why_RSS_Content_Module_is_Popular_-_Including_HTML_Contents
Atom
----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/podcastparser-0.6.4/podcastparser.py
new/podcastparser-0.6.5/podcastparser.py
--- old/podcastparser-0.6.4/podcastparser.py 2018-08-19 18:16:16.000000000
+0200
+++ new/podcastparser-0.6.5/podcastparser.py 2020-04-07 22:01:22.000000000
+0200
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
#
# Podcastparser: A simple, fast and efficient podcast parser
-# Copyright (c) 2012, 2013, 2014, 2018, Thomas Perl <[email protected]>
+# Copyright (c) 2012, 2013, 2014, 2018, 2020 Thomas Perl <[email protected]>
+# Copyright (c) 2016, 2017, 2018, 2019, 2020 Eric Le Lay <[email protected]>
+# Copyright (c) 2020 E.S. Rosenberg <[email protected]>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +22,7 @@
# Will be parsed by setup.py to determine package metadata
__author__ = 'Thomas Perl <[email protected]>'
-__version__ = '0.6.4'
+__version__ = '0.6.5'
__website__ = 'http://gpodder.org/podcastparser/'
__license__ = 'ISC License'
@@ -235,20 +237,42 @@
handler.set_episode_attr('description', squash_whitespace(text))
+class RSSItemDescription(Target):
+ """
+ RSS 2.0 almost encourages to put html content in item/description
+ but content:encoded is the better source of html content and itunes:summary
+ is known to contain the short textual description of the item.
+ So use a heuristic to attribute text to either description or
description_html,
+ without overriding existing values.
+ """
+ WANT_TEXT = True
+
+ def __init__(self):
+ self._want_content = False
+
+ def end(self, handler, text):
+ if is_html(text):
+ if not handler.get_episode_attr('description_html'):
+ handler.set_episode_attr('description_html', text.strip())
+ elif not handler.get_episode_attr('description'):
+ # don't overwrite itunes:summary?
+ handler.set_episode_attr('description', squash_whitespace(text))
+
+
class PodloveChapters(Target):
SUPPORTED_VERSIONS = ('1.1', '1.2')
def start(self, handler, attrs):
version = attrs.get('version', '1.1')
if version not in PodloveChapters.SUPPORTED_VERSIONS:
- logger.warn('Possible incompatible chapters version: %s', version)
+ logger.warning('Possible incompatible chapters version: %s',
version)
class PodloveChapter(Target):
def start(self, handler, attrs):
# Both the start and title attributes are mandatory
if attrs.get('start') is None or attrs.get('title') is None:
- logger.warn('Invalid chapter (missing start and/or and title)')
+ logger.warning('Invalid chapter (missing start and/or and title)')
return
chapter = {
@@ -361,7 +385,7 @@
namespace_uri = self.lookup(namespace)
if namespace_uri is None:
# Use of "itunes:duration" without xmlns:itunes="..."
- logger.warn('No namespace defined for "%s:%s"', namespace,
+ logger.warning('No namespace defined for "%s:%s"', namespace,
name)
return '%s:%s' % (namespace, name)
@@ -369,7 +393,7 @@
prefix = self.NAMESPACES.get(namespace_uri)
if prefix is None and namespace:
# Proper use of namespaces, but unknown namespace
- # logger.warn('Unknown namespace: %s', namespace_uri)
+ # logger.warning('Unknown namespace: %s', namespace_uri)
# We prefix the tag name here to make sure that it does not
# match any other tag below if we can't recognize the namespace
name = '!%s:%s' % (namespace, name)
@@ -466,7 +490,7 @@
try:
seconds = int(value)
except ValueError:
- logger.warn('Could not parse time value: "%s"', value)
+ logger.warning('Could not parse time value: "%s"', value)
return 0
return (int(hours) * 60 + int(minutes)) * 60 + int(seconds)
@@ -554,7 +578,7 @@
pubtimeseconds = int(mktime_tz(parsed))
return pubtimeseconds
except(OverflowError,ValueError):
- logger.warn('bad pubdate %s is before epoch or after end of time
(2038)',parsed)
+ logger.warning('bad pubdate %s is before epoch or after end of
time (2038)',parsed)
return 0
try:
@@ -596,7 +620,7 @@
'rss/channel/item/guid': EpisodeGuid('guid'),
'rss/channel/item/title': EpisodeAttr('title', squash_whitespace),
'rss/channel/item/link': EpisodeAttrRelativeLink('link'),
- 'rss/channel/item/description': EpisodeAttr('description',
squash_whitespace),
+ 'rss/channel/item/description': RSSItemDescription(),
'rss/channel/item/itunes:summary': EpisodeAttr('description',
squash_whitespace),
'rss/channel/item/media:description': EpisodeAttr('description',
squash_whitespace),
'rss/channel/item/itunes:subtitle': EpisodeAttr('subtitle',
squash_whitespace),
@@ -604,6 +628,7 @@
'rss/channel/item/itunes:duration': EpisodeAttr('total_time', parse_time),
'rss/channel/item/pubDate': EpisodeAttr('published', parse_pubdate),
'rss/channel/item/atom:link': AtomLink(),
+ 'rss/channel/item/itunes:image': EpisodeAttrFromHref('episode_art_url'),
'rss/channel/item/media:content': Enclosure('fileSize'),
'rss/channel/item/enclosure': Enclosure('length'),
@@ -614,7 +639,7 @@
'atom:feed': PodcastItem(),
'atom:feed/atom:title': PodcastAttr('title', squash_whitespace),
'atom:feed/atom:subtitle': PodcastAttr('description', squash_whitespace),
- 'atom:feed/atom:icon': PodcastAttr('cover_url'),
+ 'atom:feed/atom:icon': PodcastAttrRelativeLink('cover_url'),
'atom:feed/atom:link': PodcastAtomLink(),
'atom:feed/atom:entry': EpisodeItem(),
'atom:feed/atom:entry/atom:id': EpisodeAttr('guid'),
@@ -691,12 +716,12 @@
del entry['chapters']
# Ensures `description` does not contain HTML
- if 'description' in entry and is_html(entry['description']):
+ if is_html(entry['description']):
if 'description_html' not in entry:
entry['description_html'] = entry['description']
entry['description'] = ''
- # Sets `description` to stripped `description_html` when absent
+ # Sets `description` to stripped `description_html` when empty
if 'description_html' in entry and not entry['description']:
entry['description'] = remove_html_tags(entry['description_html'])
@@ -853,12 +878,17 @@
# urlunsplit might return "a slighty different, but equivalent URL"
return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
+HTML_TEST = re.compile('<[a-z][a-z0-9]*(?:\s.*?>|\/?>)', re.IGNORECASE |
re.DOTALL)
def is_html(text):
+ """Heuristically tell if text is HTML
+
+ By looking for an open tag (more or less:)
+ >>> is_html('<h1>HELLO</h1>')
+ True
+ >>> is_html('a < b < c')
+ False
"""
- Tests whether the given string contains HTML encoded data
- """
- html_test = re.compile(r'<[a-z][\s\S]*>', re.IGNORECASE)
- return bool(html_test.search(text))
+ return bool(HTML_TEST.search(text))
def remove_html_tags(html):
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/podcastparser-0.6.4/tests/data/atom_relative_cover.json
new/podcastparser-0.6.5/tests/data/atom_relative_cover.json
--- old/podcastparser-0.6.4/tests/data/atom_relative_cover.json 1970-01-01
01:00:00.000000000 +0100
+++ new/podcastparser-0.6.5/tests/data/atom_relative_cover.json 2020-04-07
21:57:03.000000000 +0200
@@ -0,0 +1,5 @@
+{
+ "title": "atom_relative_cover",
+ "cover_url": "file://tests/favicon.ico",
+ "episodes": []
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/podcastparser-0.6.4/tests/data/atom_relative_cover.rss
new/podcastparser-0.6.5/tests/data/atom_relative_cover.rss
--- old/podcastparser-0.6.4/tests/data/atom_relative_cover.rss 1970-01-01
01:00:00.000000000 +0100
+++ new/podcastparser-0.6.5/tests/data/atom_relative_cover.rss 2020-04-07
21:57:03.000000000 +0200
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+
<id>https://catalog.feedbooks.com/publicdomain/category/FBFIC000000/sub.atom</id>
+ <icon>/favicon.ico</icon>
+</feed>
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/podcastparser-0.6.4/tests/data/itunes_episode_image.json
new/podcastparser-0.6.5/tests/data/itunes_episode_image.json
--- old/podcastparser-0.6.4/tests/data/itunes_episode_image.json
1970-01-01 01:00:00.000000000 +0100
+++ new/podcastparser-0.6.5/tests/data/itunes_episode_image.json
2020-04-07 21:57:03.000000000 +0200
@@ -0,0 +1,16 @@
+{
+ "title": "itunes_episode_image",
+ "episodes": [
+ {
+ "guid": "file://tests/data/a",
+ "title": "Namespace Test",
+ "enclosures": [],
+ "payment_url": null,
+ "episode_art_url": "http://example.com/episode/1.jpg",
+ "link": "file://tests/data/a",
+ "published": 0,
+ "description": "",
+ "total_time": 0
+ }
+ ]
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/podcastparser-0.6.4/tests/data/itunes_episode_image.rss
new/podcastparser-0.6.5/tests/data/itunes_episode_image.rss
--- old/podcastparser-0.6.4/tests/data/itunes_episode_image.rss 1970-01-01
01:00:00.000000000 +0100
+++ new/podcastparser-0.6.5/tests/data/itunes_episode_image.rss 2020-04-07
21:57:03.000000000 +0200
@@ -0,0 +1,9 @@
+<rss xmlns:it="http://www.itunes.com/dtds/podcast-1.0.dtd">
+ <channel>
+ <item>
+ <title>Namespace Test</title>
+ <guid>a</guid>
+ <itunes:image href="http://example.com/episode/1.jpg" />
+ </item>
+ </channel>
+</rss>
++++++ python-podcastparser-remove-nose.patch ++++++
Index: podcastparser-0.6.5/test_podcastparser.py
===================================================================
--- podcastparser-0.6.5.orig/test_podcastparser.py 2020-04-07
22:00:53.000000000 +0200
+++ podcastparser-0.6.5/test_podcastparser.py 2020-07-15 16:18:39.885076713
+0200
@@ -29,14 +29,14 @@ except ImportError:
from io import StringIO
-from nose.tools import assert_equal
-from nose.tools import assert_raises
-
+import pytest
import podcastparser
-def test_rss_parsing():
- def test_parse_rss(rss_filename):
+class TestPodcastparser:
+ # test RSS parsing
+ @pytest.mark.parametrize("rss_filename", glob.glob(os.path.join('tests',
'data', '*.rss')))
+ def test_parse_rss(self, rss_filename):
basename, _ = os.path.splitext(rss_filename)
json_filename = basename + '.json'
@@ -53,21 +53,16 @@ def test_rss_parsing():
parsed = podcastparser.parse('file://' + normalized_rss_filename,
open(rss_filename), **params)
- assert_equal.__self__.maxDiff = None
- assert_equal(expected, parsed)
-
- for rss_filename in glob.glob(os.path.join('tests', 'data', '*.rss')):
- yield test_parse_rss, rss_filename
-
-def test_invalid_roots():
- def test_fail_parse(feed):
- with assert_raises(podcastparser.FeedParseError):
- podcastparser.parse('file://example.com/feed.xml', StringIO(feed))
+ assert expected == parsed
+ # test invalid roots
feeds = [
'<html><body/></html>',
'<foo xmlns="http://example.com/foo.xml"><bar/></foo>',
'<baz:foo xmlns:baz="http://example.com/baz.xml"><baz:bar/></baz:foo>',
]
- for feed in feeds:
- yield test_fail_parse, feed
+ @pytest.mark.parametrize("feed", feeds)
+ def test_fail_parse(self, feed):
+ with pytest.raises(podcastparser.FeedParseError):
+ podcastparser.parse('file://example.com/feed.xml', StringIO(feed))
+
Index: podcastparser-0.6.5/README.md
===================================================================
--- podcastparser-0.6.5.orig/README.md 2017-10-30 13:12:24.000000000 +0100
+++ podcastparser-0.6.5/README.md 2020-07-15 16:18:39.885076713 +0200
@@ -7,4 +7,4 @@ easy and reliable way of parsing RSS- an
## Automated Tests
-To run the unit tests you need
[`nose`](http://nose.readthedocs.io/en/latest/). If you have `nose` installed,
use the `nosetests` command in the repository's root directory to run the tests.
+To run the unit tests you need [`pytest`](https://docs.pytest.org/). If you
have `pytest` installed, use the `pytest` command in the repository's root
directory to run the tests.
Index: podcastparser-0.6.5/makefile
===================================================================
--- podcastparser-0.6.5.orig/makefile 2017-10-30 13:12:24.000000000 +0100
+++ podcastparser-0.6.5/makefile 2020-07-15 16:19:05.461229009 +0200
@@ -2,7 +2,7 @@ PACKAGE := podcastparser
PYTHON ?= python
FIND ?= find
-NOSETESTS ?= $(PYTHON) -m nose
+PYTEST ?= $(PYTHON) -m pytest
help:
@echo ""
@@ -12,7 +12,7 @@ help:
@echo ""
test:
- $(NOSETESTS)
+ $(PYTEST)
clean:
$(FIND) . -name '*.pyc' -o -name __pycache__ -exec $(RM) -r '{}' +
Index: podcastparser-0.6.5/requirements-test.txt
===================================================================
--- podcastparser-0.6.5.orig/requirements-test.txt 2017-10-30
13:12:24.000000000 +0100
+++ podcastparser-0.6.5/requirements-test.txt 2020-07-15 16:19:27.093357818
+0200
@@ -1,2 +1,3 @@
-nose
+pytest
+pytest-cov
coverage
Index: podcastparser-0.6.5/PKG-INFO
===================================================================
--- podcastparser-0.6.5.orig/PKG-INFO 2020-04-07 22:02:14.000000000 +0200
+++ podcastparser-0.6.5/PKG-INFO 2020-07-15 16:20:37.889779388 +0200
@@ -15,6 +15,6 @@ Description: podcastparser: Simple, fast
## Automated Tests
- To run the unit tests you need
[`nose`](http://nose.readthedocs.io/en/latest/). If you have `nose` installed,
use the `nosetests` command in the repository's root directory to run the tests.
+ To run the unit tests you need [`pytest`](http://docs.pytest.org/).
If you have `pytest` installed, use the `pytest` command in the repository's
root directory to run the tests.
Platform: UNKNOWN
Index: podcastparser-0.6.5/pytest.ini
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ podcastparser-0.6.5/pytest.ini 2020-07-15 16:22:39.186501697 +0200
@@ -0,0 +1,3 @@
+[pytest]
+addopts = --cov=podcastparser --cov-report html --doctest-modules
+
Index: podcastparser-0.6.5/setup.cfg
===================================================================
--- podcastparser-0.6.5.orig/setup.cfg 2017-10-30 13:12:24.000000000 +0100
+++ podcastparser-0.6.5/setup.cfg 2020-07-15 16:21:02.817927825 +0200
@@ -1,9 +1,3 @@
-[nosetests]
-cover-erase = true
-with-coverage = true
-cover-package = podcastparser
-with-doctest = true
-
[pep8]
max-line-length = 120
exclude = doc/conf.py