Skip to content

Commit d4fa21c

Browse files
authored
Merge pull request #125 from scrapy/py37
Python 3.7 support
2 parents 2c96a82 + 472ce65 commit d4fa21c

File tree

4 files changed

+47
-44
lines changed

4 files changed

+47
-44
lines changed

.travis.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,21 @@ matrix:
1818
env: TOXENV=py35
1919
- python: 3.6
2020
env: TOXENV=py36
21+
- python: 3.7
22+
env: TOXENV=py37
23+
dist: xenial
24+
sudo: true
2125
install:
2226
- |
2327
if [ "$TOXENV" = "pypy" ]; then
24-
export PYPY_VERSION="pypy-5.9-linux_x86_64-portable"
28+
export PYPY_VERSION="pypy-6.0.0-linux_x86_64-portable"
2529
wget "https://bitbucket.org/squeaky/portable-pypy/downloads/${PYPY_VERSION}.tar.bz2"
2630
tar -jxf ${PYPY_VERSION}.tar.bz2
2731
virtualenv --python="$PYPY_VERSION/bin/pypy" "$HOME/virtualenvs/$PYPY_VERSION"
2832
source "$HOME/virtualenvs/$PYPY_VERSION/bin/activate"
2933
fi
3034
if [ "$TOXENV" = "pypy3" ]; then
31-
export PYPY_VERSION="pypy3.5-5.9-beta-linux_x86_64-portable"
35+
export PYPY_VERSION="pypy3.5-6.0.0-linux_x86_64-portable"
3236
wget "https://bitbucket.org/squeaky/portable-pypy/downloads/${PYPY_VERSION}.tar.bz2"
3337
tar -jxf ${PYPY_VERSION}.tar.bz2
3438
virtualenv --python="$PYPY_VERSION/bin/pypy3" "$HOME/virtualenvs/$PYPY_VERSION"

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def has_environment_marker_platform_impl_support():
7272
'Programming Language :: Python :: 3.4',
7373
'Programming Language :: Python :: 3.5',
7474
'Programming Language :: Python :: 3.6',
75+
'Programming Language :: Python :: 3.7',
7576
'Programming Language :: Python :: Implementation :: CPython',
7677
'Programming Language :: Python :: Implementation :: PyPy',
7778
],

tests/test_selector.py

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,16 @@ def test_accessing_attributes(self):
114114
</html>
115115
"""
116116
sel = self.sscls(text=body)
117-
self.assertEquals({'lang': 'en', 'version': '1.0'}, sel.attrib)
118-
self.assertEquals({'id': 'some-list', 'class': 'list-cls'}, sel.css('ul')[0].attrib)
117+
self.assertEqual({'lang': 'en', 'version': '1.0'}, sel.attrib)
118+
self.assertEqual({'id': 'some-list', 'class': 'list-cls'}, sel.css('ul')[0].attrib)
119119

120120
# for a SelectorList, bring the attributes of first-element only
121-
self.assertEquals({'id': 'some-list', 'class': 'list-cls'}, sel.css('ul').attrib)
122-
self.assertEquals({'class': 'item-cls', 'id': 'list-item-1'}, sel.css('li').attrib)
123-
self.assertEquals({}, sel.css('body').attrib)
124-
self.assertEquals({}, sel.css('non-existing-element').attrib)
121+
self.assertEqual({'id': 'some-list', 'class': 'list-cls'}, sel.css('ul').attrib)
122+
self.assertEqual({'class': 'item-cls', 'id': 'list-item-1'}, sel.css('li').attrib)
123+
self.assertEqual({}, sel.css('body').attrib)
124+
self.assertEqual({}, sel.css('non-existing-element').attrib)
125125

126-
self.assertEquals(
126+
self.assertEqual(
127127
[{'class': 'item-cls', 'id': 'list-item-1'},
128128
{'class': 'item-cls active', 'id': 'list-item-2'},
129129
{'class': 'item-cls', 'id': 'list-item-3'}],
@@ -211,17 +211,17 @@ def test_re_first(self):
211211
body = u'<ul><li id="1">1</li><li id="2">2</li></ul>'
212212
sel = self.sscls(text=body)
213213

214-
self.assertEqual(sel.xpath('//ul/li/text()').re_first('\d'),
215-
sel.xpath('//ul/li/text()').re('\d')[0])
214+
self.assertEqual(sel.xpath('//ul/li/text()').re_first(r'\d'),
215+
sel.xpath('//ul/li/text()').re(r'\d')[0])
216216

217-
self.assertEqual(sel.xpath('//ul/li[@id="1"]/text()').re_first('\d'),
218-
sel.xpath('//ul/li[@id="1"]/text()').re('\d')[0])
217+
self.assertEqual(sel.xpath('//ul/li[@id="1"]/text()').re_first(r'\d'),
218+
sel.xpath('//ul/li[@id="1"]/text()').re(r'\d')[0])
219219

220-
self.assertEqual(sel.xpath('//ul/li[2]/text()').re_first('\d'),
221-
sel.xpath('//ul/li/text()').re('\d')[1])
220+
self.assertEqual(sel.xpath('//ul/li[2]/text()').re_first(r'\d'),
221+
sel.xpath('//ul/li/text()').re(r'\d')[1])
222222

223-
self.assertEqual(sel.xpath('/ul/li/text()').re_first('\w+'), None)
224-
self.assertEqual(sel.xpath('/ul/li[@id="doesnt-exist"]/text()').re_first('\d'), None)
223+
self.assertEqual(sel.xpath('/ul/li/text()').re_first(r'\w+'), None)
224+
self.assertEqual(sel.xpath('/ul/li[@id="doesnt-exist"]/text()').re_first(r'\d'), None)
225225

226226
self.assertEqual(sel.re_first(r'id="(\d+)'), '1')
227227
self.assertEqual(sel.re_first(r'foo'), None)
@@ -232,8 +232,8 @@ def test_extract_first_default(self):
232232
body = u'<ul><li id="1">1</li><li id="2">2</li></ul>'
233233
sel = self.sscls(text=body)
234234

235-
self.assertEqual(sel.xpath('//div/text()').re_first('\w+', default='missing'), 'missing')
236-
self.assertEqual(sel.xpath('/ul/li/text()').re_first('\w+', default='missing'), 'missing')
235+
self.assertEqual(sel.xpath('//div/text()').re_first(r'\w+', default='missing'), 'missing')
236+
self.assertEqual(sel.xpath('/ul/li/text()').re_first(r'\w+', default='missing'), 'missing')
237237

238238
def test_select_unicode_query(self):
239239
body = u"<p><input name='\xa9' value='1'/></p>"
@@ -249,8 +249,8 @@ def test_list_elements_type(self):
249249
def test_boolean_result(self):
250250
body = u"<p><input name='a'value='1'/><input name='b'value='2'/></p>"
251251
xs = self.sscls(text=body)
252-
self.assertEquals(xs.xpath("//input[@name='a']/@name='a'").extract(), [u'1'])
253-
self.assertEquals(xs.xpath("//input[@name='a']/@name='n'").extract(), [u'0'])
252+
self.assertEqual(xs.xpath("//input[@name='a']/@name='a'").extract(), [u'1'])
253+
self.assertEqual(xs.xpath("//input[@name='a']/@name='n'").extract(), [u'0'])
254254

255255
def test_differences_parsing_xml_vs_html(self):
256256
"""Test that XML and HTML Selector's behave differently"""
@@ -493,10 +493,10 @@ def test_re(self):
493493
</div>"""
494494
x = self.sscls(text=body)
495495

496-
name_re = re.compile("Name: (\w+)")
496+
name_re = re.compile(r"Name: (\w+)")
497497
self.assertEqual(x.xpath("//ul/li").re(name_re),
498498
["John", "Paul"])
499-
self.assertEqual(x.xpath("//ul/li").re("Age: (\d+)"),
499+
self.assertEqual(x.xpath("//ul/li").re(r"Age: (\d+)"),
500500
["10", "20"])
501501

502502
# Test named group, hit and miss
@@ -537,7 +537,7 @@ def test_re_replace_entities(self):
537537
def test_re_intl(self):
538538
body = u'<div>Evento: cumplea\xf1os</div>'
539539
x = self.sscls(text=body)
540-
self.assertEqual(x.xpath("//div").re("Evento: (\w+)"), [u'cumplea\xf1os'])
540+
self.assertEqual(x.xpath("//div").re(r"Evento: (\w+)"), [u'cumplea\xf1os'])
541541

542542
def test_selector_over_text(self):
543543
hs = self.sscls(text=u'<root>lala</root>')
@@ -568,15 +568,15 @@ def test_http_header_encoding_precedence(self):
568568
<head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"></head>
569569
<body><span id="blank">\xa3</span></body></html>'''
570570
x = self.sscls(text=text)
571-
self.assertEquals(x.xpath("//span[@id='blank']/text()").extract(),
571+
self.assertEqual(x.xpath("//span[@id='blank']/text()").extract(),
572572
[u'\xa3'])
573573

574574
def test_empty_bodies_shouldnt_raise_errors(self):
575575
self.sscls(text=u'').xpath('//text()').extract()
576576

577577
def test_bodies_with_comments_only(self):
578578
sel = self.sscls(text=u'<!-- hello world -->', base_url='http://example.com')
579-
self.assertEquals(u'http://example.com', sel.root.base)
579+
self.assertEqual(u'http://example.com', sel.root.base)
580580

581581
def test_null_bytes_shouldnt_raise_errors(self):
582582
text = u'<root>pre\x00post</root>'
@@ -585,35 +585,35 @@ def test_null_bytes_shouldnt_raise_errors(self):
585585
def test_replacement_char_from_badly_encoded_body(self):
586586
# \xe9 alone isn't valid utf8 sequence
587587
text = u'<html><p>an Jos\ufffd de</p><html>'
588-
self.assertEquals([u'an Jos\ufffd de'],
589-
self.sscls(text).xpath('//text()').extract())
588+
self.assertEqual([u'an Jos\ufffd de'],
589+
self.sscls(text).xpath('//text()').extract())
590590

591591
def test_select_on_unevaluable_nodes(self):
592592
r = self.sscls(text=u'<span class="big">some text</span>')
593593
# Text node
594594
x1 = r.xpath('//text()')
595-
self.assertEquals(x1.extract(), [u'some text'])
596-
self.assertEquals(x1.xpath('.//b').extract(), [])
595+
self.assertEqual(x1.extract(), [u'some text'])
596+
self.assertEqual(x1.xpath('.//b').extract(), [])
597597
# Tag attribute
598598
x1 = r.xpath('//span/@class')
599-
self.assertEquals(x1.extract(), [u'big'])
600-
self.assertEquals(x1.xpath('.//text()').extract(), [])
599+
self.assertEqual(x1.extract(), [u'big'])
600+
self.assertEqual(x1.xpath('.//text()').extract(), [])
601601

602602
def test_select_on_text_nodes(self):
603603
r = self.sscls(text=u'<div><b>Options:</b>opt1</div><div><b>Other</b>opt2</div>')
604604
x1 = r.xpath("//div/descendant::text()[preceding-sibling::b[contains(text(), 'Options')]]")
605-
self.assertEquals(x1.extract(), [u'opt1'])
605+
self.assertEqual(x1.extract(), [u'opt1'])
606606

607607
x1 = r.xpath("//div/descendant::text()/preceding-sibling::b[contains(text(), 'Options')]")
608-
self.assertEquals(x1.extract(), [u'<b>Options:</b>'])
608+
self.assertEqual(x1.extract(), [u'<b>Options:</b>'])
609609

610610
@unittest.skip("Text nodes lost parent node reference in lxml")
611611
def test_nested_select_on_text_nodes(self):
612612
# FIXME: does not work with lxml backend [upstream]
613613
r = self.sscls(text=u'<div><b>Options:</b>opt1</div><div><b>Other</b>opt2</div>')
614614
x1 = r.xpath("//div/descendant::text()")
615615
x2 = x1.xpath("./preceding-sibling::b[contains(text(), 'Options')]")
616-
self.assertEquals(x2.extract(), [u'<b>Options:</b>'])
616+
self.assertEqual(x2.extract(), [u'<b>Options:</b>'])
617617

618618
def test_weakref_slots(self):
619619
"""Check that classes are using slots and are weak-referenceable"""
@@ -692,7 +692,7 @@ def test_xml_entity_expansion(self):
692692

693693
def test_configure_base_url(self):
694694
sel = self.sscls(text=u'nothing', base_url='http://example.com')
695-
self.assertEquals(u'http://example.com', sel.root.base)
695+
self.assertEqual(u'http://example.com', sel.root.base)
696696

697697

698698
def test_extending_selector(self):
@@ -732,7 +732,7 @@ def test_regexp(self):
732732
self.assertEqual(
733733
[x.extract()
734734
for x in sel.xpath(
735-
'//a[re:test(@href, "\.html$")]/text()')],
735+
r'//a[re:test(@href, "\.html$")]/text()')],
736736
[u'first link', u'second link'])
737737
self.assertEqual(
738738
[x.extract()
@@ -753,20 +753,18 @@ def test_regexp(self):
753753
#u'<match></match>',
754754
#u'<match>/xml/index.xml?/xml/utils/rechecker.xml</match>']
755755
self.assertEqual(
756-
sel.xpath('re:match(//a[re:test(@href, "\.xml$")]/@href,'
757-
'"(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)")/text()').extract(),
756+
sel.xpath(r're:match(//a[re:test(@href, "\.xml$")]/@href,'
757+
r'"(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)")/text()').extract(),
758758
[u'http://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.xml',
759759
u'http',
760760
u'www.bayes.co.uk',
761761
u'',
762762
u'/xml/index.xml?/xml/utils/rechecker.xml'])
763763

764-
765-
766764
# re:replace()
767765
self.assertEqual(
768-
sel.xpath('re:replace(//a[re:test(@href, "\.xml$")]/@href,'
769-
'"(\w+)://(.+)(\.xml)", "","https://\\2.html")').extract(),
766+
sel.xpath(r're:replace(//a[re:test(@href, "\.xml$")]/@href,'
767+
r'"(\w+)://(.+)(\.xml)", "","https://\2.html")').extract(),
770768
[u'https://www.bayes.co.uk/xml/index.xml?/xml/utils/rechecker.html'])
771769

772770
def test_set(self):

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = py27, py34, py35, py36, pypy, pypy3
2+
envlist = py27, py34, py35, py36, py37, pypy, pypy3
33

44
[testenv]
55
deps =

0 commit comments

Comments
 (0)