nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: scrapy/scrapy
Pull request #1857 (2016-03-12 13:17:53)

[MRG+1] response_status_message should not fail on non-standard HTTP codes
utility is used in retry middleware and it was failing to handle non-standard HTTP codes. Instead of raising exceptions when passing through `to_native_str` it should return "Unknown status" message.


Patch:
diff --git a/scrapy/utils/response.py b/scrapy/utils/response.py
index 73db2641e..d9e951463 100644
--- a/scrapy/utils/response.py
+++ b/scrapy/utils/response.py
@@ -47,14 +47,8 @@ def get_meta_refresh(response):
 
 def response_status_message(status):
     """Return status code plus status text descriptive message
-
-    >>> response_status_message(200)
-    '200 OK'
-
-    >>> response_status_message(404)
-    '404 Not Found'
     """
-    return '%s %s' % (status, to_native_str(http.RESPONSES.get(int(status))))
+    return '%s %s' % (status, to_native_str(http.RESPONSES.get(int(status), "Unknown Status")))
 
 
 def response_httprepr(response):


Test patch:
diff --git a/tests/test_utils_response.py b/tests/test_utils_response.py
index 1d1638e06..bea4dade3 100644
--- a/tests/test_utils_response.py
+++ b/tests/test_utils_response.py
@@ -5,7 +5,7 @@ from six.moves.urllib.parse import urlparse
 from scrapy.http import Response, TextResponse, HtmlResponse
 from scrapy.utils.python import to_bytes
 from scrapy.utils.response import (response_httprepr, open_in_browser,
-                                   get_meta_refresh, get_base_url)
+                                   get_meta_refresh, get_base_url, response_status_message)
 
 __doctests__ = ['scrapy.utils.response']
 
@@ -78,3 +78,8 @@ class ResponseUtilsTest(unittest.TestCase):
         resp2 = HtmlResponse("http://www.example.com", body=b"""
         <html><body>blahablsdfsal&amp;</body></html>""")
         self.assertEqual(get_base_url(resp2), "http://www.example.com")
+
+    def test_response_status_message(self):
+        self.assertEqual(response_status_message(200), '200 OK')
+        self.assertEqual(response_status_message(404), '404 Not Found')
+        self.assertEqual(response_status_message(573), "573 Unknown Status")
Repository: scrapy/scrapy
Pull request #2050 (2016-06-12 00:47:51)

[MRG+1] Is_gzipped for application/x-gzip;charset=utf-8
Fix for #2049 


Patch:
diff --git a/scrapy/utils/gz.py b/scrapy/utils/gz.py
index d035f9fdf..cfb652143 100644
--- a/scrapy/utils/gz.py
+++ b/scrapy/utils/gz.py
@@ -7,7 +7,7 @@ except ImportError:
 from gzip import GzipFile
 
 import six
-
+import re
 
 # - Python>=3.5 GzipFile's read() has issues returning leftover
 #   uncompressed data when input is corrupted
@@ -50,8 +50,9 @@ def gunzip(data):
                 raise
     return output
 
+_is_gzipped_re = re.compile(br'^application/(x-)?gzip\b', re.I)
 
 def is_gzipped(response):
     """Return True if the response is gzipped, or False otherwise"""
     ctype = response.headers.get('Content-Type', b'')
-    return ctype in (b'application/x-gzip', b'application/gzip')
+    return _is_gzipped_re.search(ctype) is not None


Test patch:
diff --git a/tests/test_utils_gz.py b/tests/test_utils_gz.py
index 8fb1e414d..2b47bf8da 100644
--- a/tests/test_utils_gz.py
+++ b/tests/test_utils_gz.py
@@ -1,7 +1,8 @@
 import unittest
 from os.path import join
 
-from scrapy.utils.gz import gunzip
+from scrapy.utils.gz import gunzip, is_gzipped
+from scrapy.http import Response, Headers
 from tests import tests_datadir
 
 SAMPLEDIR = join(tests_datadir, 'compressed')
@@ -27,3 +28,41 @@ class GunzipTest(unittest.TestCase):
         with open(join(SAMPLEDIR, 'truncated-crc-error-short.gz'), 'rb') as f:
             text = gunzip(f.read())
             assert text.endswith(b'</html>')
+
+    def test_is_x_gzipped_right(self):
+        hdrs = Headers({"Content-Type": "application/x-gzip"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
+
+    def test_is_gzipped_right(self):
+        hdrs = Headers({"Content-Type": "application/gzip"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
+
+    def test_is_gzipped_not_quite(self):
+        hdrs = Headers({"Content-Type": "application/gzippppp"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertFalse(is_gzipped(r1))
+
+    def test_is_gzipped_case_insensitive(self):
+        hdrs = Headers({"Content-Type": "Application/X-Gzip"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
+
+        hdrs = Headers({"Content-Type": "application/X-GZIP ; charset=utf-8"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
+
+    def test_is_gzipped_empty(self):
+        r1 = Response("http://www.example.com")
+        self.assertFalse(is_gzipped(r1))
+
+    def test_is_gzipped_wrong(self):
+        hdrs = Headers({"Content-Type": "application/javascript"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertFalse(is_gzipped(r1))
+
+    def test_is_gzipped_with_charset(self):
+        hdrs = Headers({"Content-Type": "application/x-gzip;charset=utf-8"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
Repository: scrapy/scrapy
Pull request #2106 (2016-07-08 16:33:07)

[backport][1.1] Is_gzipped for application/x-gzip;charset=utf-8 (PR #2050)


Patch:
diff --git a/scrapy/utils/gz.py b/scrapy/utils/gz.py
index d035f9fdf..cfb652143 100644
--- a/scrapy/utils/gz.py
+++ b/scrapy/utils/gz.py
@@ -7,7 +7,7 @@ except ImportError:
 from gzip import GzipFile
 
 import six
-
+import re
 
 # - Python>=3.5 GzipFile's read() has issues returning leftover
 #   uncompressed data when input is corrupted
@@ -50,8 +50,9 @@ def gunzip(data):
                 raise
     return output
 
+_is_gzipped_re = re.compile(br'^application/(x-)?gzip\b', re.I)
 
 def is_gzipped(response):
     """Return True if the response is gzipped, or False otherwise"""
     ctype = response.headers.get('Content-Type', b'')
-    return ctype in (b'application/x-gzip', b'application/gzip')
+    return _is_gzipped_re.search(ctype) is not None


Test patch:
diff --git a/tests/test_utils_gz.py b/tests/test_utils_gz.py
index 8fb1e414d..2b47bf8da 100644
--- a/tests/test_utils_gz.py
+++ b/tests/test_utils_gz.py
@@ -1,7 +1,8 @@
 import unittest
 from os.path import join
 
-from scrapy.utils.gz import gunzip
+from scrapy.utils.gz import gunzip, is_gzipped
+from scrapy.http import Response, Headers
 from tests import tests_datadir
 
 SAMPLEDIR = join(tests_datadir, 'compressed')
@@ -27,3 +28,41 @@ class GunzipTest(unittest.TestCase):
         with open(join(SAMPLEDIR, 'truncated-crc-error-short.gz'), 'rb') as f:
             text = gunzip(f.read())
             assert text.endswith(b'</html>')
+
+    def test_is_x_gzipped_right(self):
+        hdrs = Headers({"Content-Type": "application/x-gzip"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
+
+    def test_is_gzipped_right(self):
+        hdrs = Headers({"Content-Type": "application/gzip"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
+
+    def test_is_gzipped_not_quite(self):
+        hdrs = Headers({"Content-Type": "application/gzippppp"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertFalse(is_gzipped(r1))
+
+    def test_is_gzipped_case_insensitive(self):
+        hdrs = Headers({"Content-Type": "Application/X-Gzip"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
+
+        hdrs = Headers({"Content-Type": "application/X-GZIP ; charset=utf-8"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
+
+    def test_is_gzipped_empty(self):
+        r1 = Response("http://www.example.com")
+        self.assertFalse(is_gzipped(r1))
+
+    def test_is_gzipped_wrong(self):
+        hdrs = Headers({"Content-Type": "application/javascript"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertFalse(is_gzipped(r1))
+
+    def test_is_gzipped_with_charset(self):
+        hdrs = Headers({"Content-Type": "application/x-gzip;charset=utf-8"})
+        r1 = Response("http://www.example.com", headers=hdrs)
+        self.assertTrue(is_gzipped(r1))
Repository: scrapy/scrapy
Pull request #2112 (2016-07-11 10:41:52)

[backport][1.1] Do not decompress binary/octet-stream responses (PR #2065)


Patch:
diff --git a/scrapy/utils/gz.py b/scrapy/utils/gz.py
index cfb652143..afc7ed128 100644
--- a/scrapy/utils/gz.py
+++ b/scrapy/utils/gz.py
@@ -50,9 +50,12 @@ def gunzip(data):
                 raise
     return output
 
-_is_gzipped_re = re.compile(br'^application/(x-)?gzip\b', re.I)
+_is_gzipped = re.compile(br'^application/(x-)?gzip\b', re.I).search
+_is_octetstream = re.compile(br'^(application|binary)/octet-stream\b', re.I).search
 
 def is_gzipped(response):
     """Return True if the response is gzipped, or False otherwise"""
     ctype = response.headers.get('Content-Type', b'')
-    return _is_gzipped_re.search(ctype) is not None
+    cenc = response.headers.get('Content-Encoding', b'').lower()
+    return (_is_gzipped(ctype) or
+            (_is_octetstream(ctype) and cenc in (b'gzip', b'x-gzip')))


Test patch:
diff --git a/tests/test_downloadermiddleware_httpcompression.py b/tests/test_downloadermiddleware_httpcompression.py
index 24955a515..b2426946d 100644
--- a/tests/test_downloadermiddleware_httpcompression.py
+++ b/tests/test_downloadermiddleware_httpcompression.py
@@ -145,6 +145,26 @@ class HttpCompressionTest(TestCase):
         self.assertEqual(response.headers['Content-Encoding'], b'gzip')
         self.assertEqual(response.headers['Content-Type'], b'application/gzip')
 
+    def test_process_response_gzip_app_octetstream_contenttype(self):
+        response = self._getresponse('gzip')
+        response.headers['Content-Type'] = 'application/octet-stream'
+        request = response.request
+
+        newresponse = self.mw.process_response(request, response, self.spider)
+        self.assertIs(newresponse, response)
+        self.assertEqual(response.headers['Content-Encoding'], b'gzip')
+        self.assertEqual(response.headers['Content-Type'], b'application/octet-stream')
+
+    def test_process_response_gzip_binary_octetstream_contenttype(self):
+        response = self._getresponse('x-gzip')
+        response.headers['Content-Type'] = 'binary/octet-stream'
+        request = response.request
+
+        newresponse = self.mw.process_response(request, response, self.spider)
+        self.assertIs(newresponse, response)
+        self.assertEqual(response.headers['Content-Encoding'], b'gzip')
+        self.assertEqual(response.headers['Content-Type'], b'binary/octet-stream')
+
     def test_process_response_head_request_no_decode_required(self):
         response = self._getresponse('gzip')
         response.headers['Content-Type'] = 'application/gzip'
Repository: scrapy/scrapy
Pull request #2433 (2016-12-08 11:52:07)

[MRG+1] Warn user instead of failing for wrong SPIDER_MODULES setting
Proposal fix for #2430

Example output:

```
$ scrapy version
/home/paul/src/scrapy/scrapy/spiderloader.py:36: RuntimeWarning: Could not load spiders from module 'version_wrong_spiders.spiderssss'; Check SPIDER_MODULES setting (exception: No module named spiderssss)
  warnings.warn(msg, RuntimeWarning)
Scrapy 1.2.2
```


Patch:
diff --git a/scrapy/spiderloader.py b/scrapy/spiderloader.py
index fbf68cec4..d4f0f663f 100644
--- a/scrapy/spiderloader.py
+++ b/scrapy/spiderloader.py
@@ -1,5 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import
+import traceback
+import warnings
 
 from zope.interface import implementer
 
@@ -18,15 +20,21 @@ class SpiderLoader(object):
         self.spider_modules = settings.getlist('SPIDER_MODULES')
         self._spiders = {}
         self._load_all_spiders()
-            
+
     def _load_spiders(self, module):
         for spcls in iter_spider_classes(module):
             self._spiders[spcls.name] = spcls
 
     def _load_all_spiders(self):
         for name in self.spider_modules:
-            for module in walk_modules(name):
-                self._load_spiders(module)
+            try:
+                for module in walk_modules(name):
+                    self._load_spiders(module)
+            except ImportError as e:
+                msg = ("\n{tb}Could not load spiders from module '{modname}'. "
+                       "Check SPIDER_MODULES setting".format(
+                            modname=name, tb=traceback.format_exc()))
+                warnings.warn(msg, RuntimeWarning)
 
     @classmethod
     def from_settings(cls, settings):


Test patch:
diff --git a/tests/test_spiderloader/__init__.py b/tests/test_spiderloader/__init__.py
index fbd2c1669..b2ad93b3f 100644
--- a/tests/test_spiderloader/__init__.py
+++ b/tests/test_spiderloader/__init__.py
@@ -1,6 +1,7 @@
 import sys
 import os
 import shutil
+import warnings
 
 from zope.interface.verify import verifyObject
 from twisted.trial import unittest
@@ -89,3 +90,14 @@ class SpiderLoaderTest(unittest.TestCase):
         crawler = runner.create_crawler('spider1')
         self.assertTrue(issubclass(crawler.spidercls, scrapy.Spider))
         self.assertEqual(crawler.spidercls.name, 'spider1')
+
+    def test_bad_spider_modules_warning(self):
+
+        with warnings.catch_warnings(record=True) as w:
+            module = 'tests.test_spiderloader.test_spiders.doesnotexist'
+            settings = Settings({'SPIDER_MODULES': [module]})
+            spider_loader = SpiderLoader.from_settings(settings)
+            self.assertIn("Could not load spiders from module", str(w[0].message))
+
+            spiders = spider_loader.list()
+            self.assertEqual(spiders, [])
Repository: scrapy/scrapy
Pull request #3011 (2017-11-18 19:07:00)

[MRG+1] Issues a warning when user puts a URL into allowed_domains (#2250)


Patch:
diff --git a/scrapy/spidermiddlewares/offsite.py b/scrapy/spidermiddlewares/offsite.py
index ea1c9270f..310166cad 100644
--- a/scrapy/spidermiddlewares/offsite.py
+++ b/scrapy/spidermiddlewares/offsite.py
@@ -6,6 +6,7 @@ See documentation in docs/topics/spider-middleware.rst
 
 import re
 import logging
+import warnings
 
 from scrapy import signals
 from scrapy.http import Request
@@ -52,9 +53,18 @@ class OffsiteMiddleware(object):
         allowed_domains = getattr(spider, 'allowed_domains', None)
         if not allowed_domains:
             return re.compile('') # allow all by default
+        url_pattern = re.compile("^https?://.*$")
+        for domain in allowed_domains:
+            if url_pattern.match(domain):
+                warnings.warn("allowed_domains accepts only domains, not URLs. Ignoring URL entry %s in allowed_domains." % domain, URLWarning)
+                
         regex = r'^(.*\.)?(%s)$' % '|'.join(re.escape(d) for d in allowed_domains if d is not None)
         return re.compile(regex)
 
     def spider_opened(self, spider):
         self.host_regex = self.get_host_regex(spider)
         self.domains_seen = set()
+
+
+class URLWarning(Warning):
+    pass


Test patch:
diff --git a/tests/test_spidermiddleware_offsite.py b/tests/test_spidermiddleware_offsite.py
index 9ad86313c..7e4af0d4c 100644
--- a/tests/test_spidermiddleware_offsite.py
+++ b/tests/test_spidermiddleware_offsite.py
@@ -5,7 +5,9 @@ from six.moves.urllib.parse import urlparse
 from scrapy.http import Response, Request
 from scrapy.spiders import Spider
 from scrapy.spidermiddlewares.offsite import OffsiteMiddleware
+from scrapy.spidermiddlewares.offsite import URLWarning
 from scrapy.utils.test import get_crawler
+import warnings
 
 class TestOffsiteMiddleware(TestCase):
 
@@ -68,3 +70,13 @@ class TestOffsiteMiddleware4(TestOffsiteMiddleware3):
       reqs = [Request('http://scrapytest.org/1')]
       out = list(self.mw.process_spider_output(res, reqs, self.spider))
       self.assertEqual(out, reqs)
+
+
+class TestOffsiteMiddleware5(TestOffsiteMiddleware4):
+    
+    def test_get_host_regex(self):
+        self.spider.allowed_domains = ['http://scrapytest.org', 'scrapy.org', 'scrapy.test.org']
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+            self.mw.get_host_regex(self.spider)
+            assert issubclass(w[-1].category, URLWarning)
Repository: scrapy/scrapy
Pull request #3131 (2018-02-21 00:26:57)

[MRG+1] Fix part of issue #3128 - None should not be a valid type for 'url' in Response.follow
the issue reason should be: 'urljoin from urllib return baseurl if targeturl is None'

the empty url issue haven't been touched because @kmike says: 'this is an intended behavior.'

codecov[bot]: # [Codecov](https://codecov.io/gh/scrapy/scrapy/pull/3131?src=pr&el=h1) Report
> Merging [#3131](https://codecov.io/gh/scrapy/scrapy/pull/3131?src=pr&el=desc) into [master](https://codecov.io/gh/scrapy/scrapy/commit/426da0ed07637e7efbcfb0fe49546e187d5d7f67?src=pr&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `0%`.


```diff
@@            Coverage Diff             @@
##           master    #3131      +/-   ##
==========================================
- Coverage    82.1%   82.09%   -0.02%     
==========================================
  Files         228      228              
  Lines        9586     9588       +2     
  Branches     1384     1385       +1     
==========================================
  Hits         7871     7871              
- Misses       1456     1457       +1     
- Partials      259      260       +1
```

| [Impacted Files](https://codecov.io/gh/scrapy/scrapy/pull/3131?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [scrapy/http/response/\_\_init\_\_.py](https://codecov.io/gh/scrapy/scrapy/pull/3131/diff?src=pr&el=tree#diff-c2NyYXB5L2h0dHAvcmVzcG9uc2UvX19pbml0X18ucHk=) | `90.16% <0%> (-3.06%)` | :arrow_down: |

kmike: Thanks @NewUserHa!

Patch:
diff --git a/scrapy/http/response/__init__.py b/scrapy/http/response/__init__.py
index 434d87eab..1974259b5 100644
--- a/scrapy/http/response/__init__.py
+++ b/scrapy/http/response/__init__.py
@@ -120,6 +120,8 @@ class Response(object_ref):
         """
         if isinstance(url, Link):
             url = url.url
+        elif url is None:
+            raise ValueError("url can't be None")
         url = self.urljoin(url)
         return Request(url, callback,
                        method=method,


Test patch:
diff --git a/tests/test_http_response.py b/tests/test_http_response.py
index b228344b5..820758dc9 100644
--- a/tests/test_http_response.py
+++ b/tests/test_http_response.py
@@ -155,6 +155,10 @@ class BaseResponseTest(unittest.TestCase):
         self._assert_followed_url(Link('http://example.com/foo'),
                                   'http://example.com/foo')
 
+    def test_follow_None_url(self):
+        r = self.response_class("http://example.com")
+        self.assertRaises(ValueError, r.follow, None)
+
     def test_follow_whitespace_url(self):
         self._assert_followed_url('foo ',
                                   'http://example.com/foo%20')
Repository: scrapy/scrapy
Pull request #3153 (2018-03-03 12:42:07)

[MRG+1] Fixed bug FormRequest.from_response() clickdata ignores input[type=image]
BugFix for issue #3139 


Patch:
diff --git a/scrapy/http/request/form.py b/scrapy/http/request/form.py
index d9d178a3e..95b38e990 100644
--- a/scrapy/http/request/form.py
+++ b/scrapy/http/request/form.py
@@ -170,9 +170,8 @@ def _get_clickable(clickdata, form):
     """
     clickables = [
         el for el in form.xpath(
-            'descendant::*[(self::input or self::button)'
-            ' and re:test(@type, "^submit$", "i")]'
-            '|descendant::button[not(@type)]',
+            'descendant::input[re:test(@type, "^(submit|image)$", "i")]'
+            '|descendant::button[not(@type) or re:test(@type, "^submit$", "i")]',
             namespaces={"re": "http://exslt.org/regular-expressions"})
         ]
     if not clickables:


Test patch:
diff --git a/tests/test_http_request.py b/tests/test_http_request.py
index fca8ff411..a042f03b6 100644
--- a/tests/test_http_request.py
+++ b/tests/test_http_request.py
@@ -544,6 +544,16 @@ class FormRequestTest(RequestTest):
         req = self.request_class.from_response(response, dont_click=True)
         fs = _qs(req)
         self.assertEqual(fs, {b'i1': [b'i1v'], b'i2': [b'i2v']})
+    
+    def test_from_response_clickdata_does_not_ignore_image(self):
+        response = _buildresponse(
+            """<form>
+            <input type="text" name="i1" value="i1v">
+            <input id="image" name="i2" type="image" value="i2v" alt="Login" src="http://my.image.org/1.jpg">
+            </form>""")
+        req = self.request_class.from_response(response)
+        fs = _qs(req)
+        self.assertEqual(fs, {b'i1': [b'i1v'], b'i2': [b'i2v']})
 
     def test_from_response_multiple_clickdata(self):
         response = _buildresponse(
Repository: scrapy/scrapy
Pull request #3342 (2018-07-19 15:04:22)

[MRG+1] Include flags when copying requests
I noticed flags were missing after a request was retried by the retry downloader middleware, traced the issue and realized that they were not being copied.
Small test case included.


Patch:
diff --git a/scrapy/http/request/__init__.py b/scrapy/http/request/__init__.py
index 13a92ffa0..cd4360483 100644
--- a/scrapy/http/request/__init__.py
+++ b/scrapy/http/request/__init__.py
@@ -91,7 +91,7 @@ class Request(object_ref):
         """Create a new Request with the same attributes except for those
         given new values.
         """
-        for x in ['url', 'method', 'headers', 'body', 'cookies', 'meta',
+        for x in ['url', 'method', 'headers', 'body', 'cookies', 'meta', 'flags',
                   'encoding', 'priority', 'dont_filter', 'callback', 'errback']:
             kwargs.setdefault(x, getattr(self, x))
         cls = kwargs.pop('cls', self.__class__)


Test patch:
diff --git a/tests/test_http_request.py b/tests/test_http_request.py
index a042f03b6..fc89229c6 100644
--- a/tests/test_http_request.py
+++ b/tests/test_http_request.py
@@ -174,7 +174,8 @@ class RequestTest(unittest.TestCase):
         def somecallback():
             pass
 
-        r1 = self.request_class("http://www.example.com", callback=somecallback, errback=somecallback)
+        r1 = self.request_class("http://www.example.com", flags=['f1', 'f2'],
+                                callback=somecallback, errback=somecallback)
         r1.meta['foo'] = 'bar'
         r2 = r1.copy()
 
@@ -184,6 +185,10 @@ class RequestTest(unittest.TestCase):
         assert r2.callback is r1.callback
         assert r2.errback is r2.errback
 
+        # make sure flags list is shallow copied
+        assert r1.flags is not r2.flags, "flags must be a shallow copy, not identical"
+        self.assertEqual(r1.flags, r2.flags)
+
         # make sure meta dict is shallow copied
         assert r1.meta is not r2.meta, "meta must be a shallow copy, not identical"
         self.assertEqual(r1.meta, r2.meta)