nficano commited on
Commit
22b0f22
·
1 Parent(s): f077f79
.gitignore CHANGED
@@ -28,3 +28,4 @@ doc/_build
28
  doc/aws_hostname.1
29
 
30
  .coverage
 
 
28
  doc/aws_hostname.1
29
 
30
  .coverage
31
+ .cache
.travis.yml CHANGED
@@ -9,13 +9,11 @@ python:
9
  - "3.6-dev"
10
  - "3.7-dev"
11
  - "nightly"
12
- # command to install dependencies
13
- install: "pip install -r tests/requirements.txt"
14
- # command to run tests
15
- script: nosetests
 
16
  sudo: required
17
- branches:
18
- only:
19
- - master
20
  after_success:
21
  coveralls
 
9
  - "3.6-dev"
10
  - "3.7-dev"
11
  - "nightly"
12
+ install:
13
+ - pip install -r requirements.txt
14
+ - pip install -r tests/requirements.txt
15
+ before_script: flake8
16
+ script: pytest --cov=pytest
17
  sudo: required
 
 
 
18
  after_success:
19
  coveralls
pytube/jsinterp.py CHANGED
@@ -120,8 +120,10 @@ class JSInterpreter(object):
120
  except ValueError:
121
  pass
122
 
123
- m = re.match(r'(?P<var>%s)\.(?P<member>[^(]+)'
124
- r'(?:\(+(?P<args>[^()]*)\))?$' % _NAME_RE, expr)
 
 
125
  if m:
126
  variable = m.group('var')
127
  member = m.group('member')
@@ -213,9 +215,8 @@ class JSInterpreter(object):
213
  obj = {}
214
  obj_m = re.search(
215
  (r'(?:var\s+)?%s\s*=\s*\{' % re.escape(objname)) +
216
- r'\s*(?P<fields>([a-zA-Z$0-9]+\s*:\s*function\(.*?\)\s*\'' +
217
- r'{.*?\}(?:,\s*)?)*)' +
218
- r'\}\s*;',
219
  self.code)
220
  fields = obj_m.group('fields')
221
  # Currently, it only supports function definitions
 
120
  except ValueError:
121
  pass
122
 
123
+ m = re.match(
124
+ r'(?P<var>%s)\.(?P<member>[^(]+)'
125
+ '(?:\(+(?P<args>[^()]*)\))?$' % _NAME_RE,
126
+ expr)
127
  if m:
128
  variable = m.group('var')
129
  member = m.group('member')
 
215
  obj = {}
216
  obj_m = re.search(
217
  (r'(?:var\s+)?%s\s*=\s*\{' % re.escape(objname)) +
218
+ r'\s*(?P<fields>([a-zA-Z$0-9]+\s*:\s*function\(.*?\)\s*\{.*?\}'
219
+ r'(?:,\s*)?)*)' + r'\}\s*;',
 
220
  self.code)
221
  fields = obj_m.group('fields')
222
  # Currently, it only supports function definitions
tests/conftest.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import mock
2
+ import pytest
3
+
4
+ from pytube import api
5
+
6
+
7
+ @pytest.fixture
8
+ def yt_video():
9
+ url = 'http://www.youtube.com/watch?v=9bZkp7q19f0'
10
+ mock_html = None
11
+ mock_js = None
12
+
13
+ with open('tests/mock_data/youtube_gangnam_style.html') as fh:
14
+ mock_html = fh.read()
15
+
16
+ with open('tests/mock_data/youtube_gangnam_style.js') as fh:
17
+ mock_js = fh.read()
18
+
19
+ with mock.patch('pytube.api.urlopen') as urlopen:
20
+ urlopen.return_value.read.return_value = mock_html
21
+ yt = api.YouTube()
22
+ yt._js_cache = mock_js
23
+ yt.from_url(url)
24
+ return yt
tests/requirements.txt CHANGED
@@ -1,5 +1,6 @@
1
  bumpversion==0.5.3
2
  coveralls==1.0
3
  mock==1.3.0
4
- nose==1.3.7
5
  pre-commit==0.15.0
 
 
 
1
  bumpversion==0.5.3
2
  coveralls==1.0
3
  mock==1.3.0
 
4
  pre-commit==0.15.0
5
+ pytest==3.1.3
6
+ pytest-cov==2.4.0
tests/test_pytube.py CHANGED
@@ -1,140 +1,130 @@
1
  #!/usr/bin/env python
2
  # -*- coding: utf-8 -*-
3
- from __future__ import unicode_literals
4
-
5
  import warnings
6
 
7
  import mock
8
- from nose.tools import eq_
9
- from nose.tools import raises
10
 
11
  from pytube import api
12
  from pytube.exceptions import AgeRestricted
13
- from pytube.exceptions import DoesNotExist
14
  from pytube.exceptions import MultipleObjectsReturned
15
  from pytube.exceptions import PytubeError
16
 
17
 
18
- class TestPytube(object):
19
- def setUp(self):
20
- url = 'http://www.youtube.com/watch?v=9bZkp7q19f0'
21
-
22
- with open('tests/mock_data/youtube_gangnam_style.html') as fh:
23
- self.mock_html = fh.read()
24
-
25
- with open('tests/mock_data/youtube_gangnam_style.js') as fh:
26
- self.mock_js = fh.read()
27
-
28
- with mock.patch('pytube.api.urlopen') as urlopen:
29
- urlopen.return_value.read.return_value = self.mock_html
30
- self.yt = api.YouTube()
31
- self.yt._js_cache = self.mock_js
32
- self.yt.from_url(url)
33
-
34
- def test_get_video_id(self):
35
- """Resolve the video id from url"""
36
- eq_(self.yt.video_id, '9bZkp7q19f0')
37
-
38
- def test_auto_filename(self):
39
- """Generate safe filename based on video title"""
40
- expected = 'PSY - GANGNAM STYLE(\uac15\ub0a8\uc2a4\ud0c0\uc77c) MV'
41
-
42
- eq_(self.yt.filename, expected)
43
-
44
- def test_manual_filename(self):
45
- """Manually set a filename"""
46
- expected = 'PSY - Gangnam Style'
47
-
48
- self.yt.set_filename(expected)
49
- eq_(self.yt.filename, expected)
50
-
51
- def test_get_all_videos(self):
52
- """Get all videos"""
53
- eq_(len(self.yt.get_videos()), 6)
54
-
55
- def test_filter_video_by_extension(self):
56
- """Filter videos by filetype"""
57
- eq_(len(self.yt.filter('mp4')), 2)
58
- eq_(len(self.yt.filter('3gp')), 2)
59
- eq_(len(self.yt.filter('webm')), 1)
60
- eq_(len(self.yt.filter('flv')), 1)
61
-
62
- def test_filter_video_by_extension_and_resolution(self):
63
- """Filter videos by file extension and resolution"""
64
- eq_(len(self.yt.filter('mp4', '720p')), 1)
65
- eq_(len(self.yt.filter('mp4', '1080p')), 0)
66
-
67
- def test_filter_video_by_extension_resolution_profile(self):
68
- """Filter videos by file extension, resolution, and profile"""
69
- eq_(len(self.yt.filter('mp4', '360p', 'Baseline')), 1)
70
-
71
- def test_filter_video_by_profile(self):
72
- """Filter videos by file profile"""
73
- eq_(len(self.yt.filter(profile='Simple')), 2)
74
-
75
- def test_filter_video_by_resolution(self):
76
- """Filter videos by resolution"""
77
- eq_(len(self.yt.filter(resolution='360p')), 2)
78
-
79
- @raises(MultipleObjectsReturned)
80
- def test_get_multiple_items(self):
81
- """get(...) cannot return more than one video"""
82
- self.yt.get(profile='Simple')
83
- self.yt.get('mp4')
84
- self.yt.get(resolution='240p')
85
-
86
- @raises(DoesNotExist)
87
- def test_get_does_not_exist(self):
88
- """get(...) must return something"""
89
- self.yt.get('mp4', '1080p')
90
-
91
- @raises(AgeRestricted)
92
- def test_age_restricted_video(self):
93
- """Raise exception on age restricted video"""
94
- url = 'http://www.youtube.com/watch?v=nzNgkc6t260'
95
-
96
- with open('tests/mock_data/youtube_age_restricted.html') as fh:
97
- mock_html = fh.read()
98
-
99
- with mock.patch('pytube.api.urlopen') as urlopen:
100
- urlopen.return_value.read.return_value = mock_html
101
- yt = api.YouTube()
102
- yt.from_url(url)
103
-
104
- def test_deprecation_warnings_on_url_set(self):
105
- """Deprecation warnings get triggered on url set"""
106
- with warnings.catch_warnings(record=True) as w:
107
- # Cause all warnings to always be triggered.
108
- warnings.simplefilter('always')
109
- with mock.patch('pytube.api.urlopen') as urlopen:
110
- urlopen.return_value.read.return_value = self.mock_html
111
- yt = api.YouTube()
112
- yt._js_cache = self.mock_js
113
- yt.url = 'http://www.youtube.com/watch?v=9bZkp7q19f0'
114
- eq_(len(w), 1)
115
-
116
- def test_deprecation_warnings_on_filename_set(self):
117
- """Deprecation warnings get triggered on filename set"""
118
- with warnings.catch_warnings(record=True) as w:
119
- # Cause all warnings to always be triggered.
120
- warnings.simplefilter('always')
121
- self.yt.filename = 'Gangnam Style'
122
- eq_(len(w), 1)
123
-
124
- def test_deprecation_warnings_on_videos_get(self):
125
- """Deprecation warnings get triggered on video getter"""
126
- with warnings.catch_warnings(record=True) as w:
127
- # Cause all warnings to always be triggered.
128
- warnings.simplefilter('always')
129
- self.yt.videos
130
- eq_(len(w), 1)
131
-
132
- def test_get_json_offset(self):
133
- """Find the offset in the html for where the js starts"""
134
- offset = self.yt._get_json_offset(self.mock_html)
135
- eq_(offset, 312)
136
-
137
- @raises(PytubeError)
138
- def test_get_json_offset_with_bad_html(self):
139
- """Raise exception if json offset cannot be found"""
140
- self.yt._get_json_offset('asdfasdf')
 
1
  #!/usr/bin/env python
2
  # -*- coding: utf-8 -*-
 
 
3
  import warnings
4
 
5
  import mock
6
+ import pytest
 
7
 
8
  from pytube import api
9
  from pytube.exceptions import AgeRestricted
 
10
  from pytube.exceptions import MultipleObjectsReturned
11
  from pytube.exceptions import PytubeError
12
 
13
 
14
+ def test_video_id(yt_video):
15
+ """Resolve the video id from url"""
16
+ assert yt_video.video_id == '9bZkp7q19f0'
17
+
18
+
19
+ def test_auto_filename(yt_video):
20
+ """Generate safe filename based on video title"""
21
+ expected = 'PSY - GANGNAM STYLE(\uac15\ub0a8\uc2a4\ud0c0\uc77c) MV'
22
+ assert yt_video.filename == expected
23
+
24
+
25
+ def test_manual_filename(yt_video):
26
+ """Manually set a filename"""
27
+ expected = 'PSY - Gangnam Style'
28
+ yt_video.set_filename(expected)
29
+ assert yt_video.filename == expected
30
+
31
+
32
+ def test_get_all_videos(yt_video):
33
+ """Get all videos"""
34
+ assert len(yt_video.get_videos()) == 6
35
+
36
+
37
+ def test_filter_video_by_extension(yt_video):
38
+ """Filter videos by filetype"""
39
+ assert len(yt_video.filter('mp4')) == 2
40
+ assert len(yt_video.filter('3gp')) == 2
41
+ assert len(yt_video.filter('webm')) == 1
42
+ assert len(yt_video.filter('flv')) == 1
43
+
44
+
45
+ def test_filter_video_by_extension_and_resolution(yt_video):
46
+ """Filter videos by file extension and resolution"""
47
+ assert len(yt_video.filter('mp4', '720p')) == 1
48
+ assert len(yt_video.filter('mp4', '1080p')) == 0
49
+
50
+
51
+ def test_filter_video_by_extension_resolution_profile(yt_video):
52
+ """Filter videos by file extension, resolution, and profile"""
53
+ assert len(yt_video.filter('mp4', '360p', 'Baseline')) == 1
54
+
55
+
56
+ def test_filter_video_by_profile(yt_video):
57
+ """Filter videos by file profile"""
58
+ assert len(yt_video.filter(profile='Simple')) == 2
59
+
60
+
61
+ def test_filter_video_by_resolution(yt_video):
62
+ """Filter videos by resolution"""
63
+ assert len(yt_video.filter(resolution='360p')) == 2
64
+
65
+
66
+ def test_get_multiple_items(yt_video):
67
+ """get(...) cannot return more than one video"""
68
+ with pytest.raises(MultipleObjectsReturned):
69
+ yt_video.get(profile='Simple')
70
+ yt_video.get('mp4')
71
+ yt_video.get(resolution='240p')
72
+
73
+
74
+ def test_age_restricted_video():
75
+ """Raise exception on age restricted video"""
76
+ mock_html = None
77
+
78
+ with open('tests/mock_data/youtube_age_restricted.html') as fh:
79
+ mock_html = fh.read()
80
+
81
+ with mock.patch('pytube.api.urlopen') as urlopen:
82
+ urlopen.return_value.read.return_value = mock_html
83
+ yt = api.YouTube()
84
+
85
+ with pytest.raises(AgeRestricted):
86
+ yt.from_url('http://www.youtube.com/watch?v=nzNgkc6t260')
87
+
88
+
89
+ def test_deprecation_warnings_on_url_set(yt_video):
90
+ """Deprecation warnings get triggered on url set"""
91
+ with warnings.catch_warnings(record=True) as w:
92
+ # Cause all warnings to always be triggered.
93
+ warnings.simplefilter('always')
94
+ yt_video.url = 'http://www.youtube.com/watch?v=9bZkp7q19f0'
95
+ assert len(w) == 1
96
+
97
+
98
+ def test_deprecation_warnings_on_filename_set(yt_video):
99
+ """Deprecation warnings get triggered on filename set"""
100
+ with warnings.catch_warnings(record=True) as w:
101
+ # Cause all warnings to always be triggered.
102
+ warnings.simplefilter('always')
103
+ yt_video.filename = 'Gangnam Style'
104
+ assert len(w) == 1
105
+
106
+
107
+ def test_deprecation_warnings_on_videos_get(yt_video):
108
+ """Deprecation warnings get triggered on video getter"""
109
+ with warnings.catch_warnings(record=True) as w:
110
+ # Cause all warnings to always be triggered.
111
+ warnings.simplefilter('always')
112
+ yt_video.videos
113
+ assert len(w) == 1
114
+
115
+
116
+ def test_get_json_offset(yt_video):
117
+ """Find the offset in the html for where the js starts"""
118
+ mock_html = None
119
+
120
+ with open('tests/mock_data/youtube_gangnam_style.html') as fh:
121
+ mock_html = fh.read()
122
+
123
+ offset = yt_video._get_json_offset(mock_html)
124
+ assert offset == 312
125
+
126
+
127
+ def test_get_json_offset_with_bad_html(yt_video):
128
+ """Raise exception if json offset cannot be found"""
129
+ with pytest.raises(PytubeError):
130
+ yt_video._get_json_offset('asdfasdf')
 
 
 
 
 
 
tests/test_utils.py CHANGED
@@ -2,32 +2,32 @@
2
  # -*- coding: utf-8 -*-
3
  from __future__ import unicode_literals
4
 
5
- from nose.tools import eq_
6
-
7
  from pytube import utils
8
 
9
 
10
- class TestUtils(object):
11
- blob = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'
12
-
13
- def test_truncate(self):
14
- """Truncate string works as expected"""
15
- truncated = utils.truncate(self.blob, 11)
16
- eq_(truncated, 'Lorem ipsum')
17
-
18
- def test_safe_filename(self):
19
- """Unsafe characters get stripped from generated filename"""
20
- eq_(utils.safe_filename('abc1245$$'), 'abc1245')
21
- eq_(utils.safe_filename('abc##'), 'abc')
22
- eq_(utils.safe_filename('abc:foo'), 'abc -foo')
23
- eq_(utils.safe_filename('abc_foo'), 'abc foo')
24
-
25
- def test_sizeof(self):
26
- """Accurately converts the bytes to its humanized equivalent"""
27
- eq_(utils.sizeof(1), '1 byte')
28
- eq_(utils.sizeof(2), '2 bytes')
29
- eq_(utils.sizeof(2400), '2 KB')
30
- eq_(utils.sizeof(2400000), '2 MB')
31
- eq_(utils.sizeof(2400000000), '2 GB')
32
- eq_(utils.sizeof(2400000000000), '2 TB')
33
- eq_(utils.sizeof(2400000000000000), '2 PB')
 
 
 
2
  # -*- coding: utf-8 -*-
3
  from __future__ import unicode_literals
4
 
 
 
5
  from pytube import utils
6
 
7
 
8
+ blob = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'
9
+
10
+
11
+ def test_truncate():
12
+ """Truncate string works as expected"""
13
+ truncated = utils.truncate(blob, 11)
14
+ assert truncated == 'Lorem ipsum'
15
+
16
+
17
+ def test_safe_filename():
18
+ """Unsafe characters get stripped from generated filename"""
19
+ assert utils.safe_filename('abc1245$$') == 'abc1245'
20
+ assert utils.safe_filename('abc##') == 'abc'
21
+ assert utils.safe_filename('abc:foo') == 'abc -foo'
22
+ assert utils.safe_filename('abc_foo') == 'abc foo'
23
+
24
+
25
+ def test_sizeof():
26
+ """Accurately converts the bytes to its humanized equivalent"""
27
+ assert utils.sizeof(1) == '1 byte'
28
+ assert utils.sizeof(2) == '2 bytes'
29
+ assert utils.sizeof(2400) == '2 KB'
30
+ assert utils.sizeof(2400000) == '2 MB'
31
+ assert utils.sizeof(2400000000) == '2 GB'
32
+ assert utils.sizeof(2400000000000) == '2 TB'
33
+ assert utils.sizeof(2400000000000000) == '2 PB'