We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support pathlib.Path in FEED_URI
Make things work the same when the value assigned to the `FEED_URI` setting is a string containing a path or an instance of `pathlib.Path`.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scrapy/extensions/feedexport.py`
Content:
```
1 """
2 Feed Exports extension
3 
4 See documentation in docs/topics/feed-exports.rst
5 """
6 
7 import os
8 import sys
9 import logging
10 import posixpath
11 from tempfile import NamedTemporaryFile
12 from datetime import datetime
13 import six
14 from six.moves.urllib.parse import urlparse, unquote
15 from ftplib import FTP
16 
17 from zope.interface import Interface, implementer
18 from twisted.internet import defer, threads
19 from w3lib.url import file_uri_to_path
20 
21 from scrapy import signals
22 from scrapy.utils.ftp import ftp_makedirs_cwd
23 from scrapy.exceptions import NotConfigured
24 from scrapy.utils.misc import create_instance, load_object
25 from scrapy.utils.log import failure_to_exc_info
26 from scrapy.utils.python import without_none_values
27 from scrapy.utils.boto import is_botocore
28 
29 logger = logging.getLogger(__name__)
30 
31 
32 class IFeedStorage(Interface):
33     """Interface that all Feed Storages must implement"""
34 
35     def __init__(uri):
36         """Initialize the storage with the parameters given in the URI"""
37 
38     def open(spider):
39         """Open the storage for the given spider. It must return a file-like
40         object that will be used for the exporters"""
41 
42     def store(file):
43         """Store the given file stream"""
44 
45 
46 @implementer(IFeedStorage)
47 class BlockingFeedStorage(object):
48 
49     def open(self, spider):
50         path = spider.crawler.settings['FEED_TEMPDIR']
51         if path and not os.path.isdir(path):
52             raise OSError('Not a Directory: ' + str(path))
53 
54         return NamedTemporaryFile(prefix='feed-', dir=path)
55 
56     def store(self, file):
57         return threads.deferToThread(self._store_in_thread, file)
58 
59     def _store_in_thread(self, file):
60         raise NotImplementedError
61 
62 
63 @implementer(IFeedStorage)
64 class StdoutFeedStorage(object):
65 
66     def __init__(self, uri, _stdout=None):
67         if not _stdout:
68             _stdout = sys.stdout if six.PY2 else sys.stdout.buffer
69         self._stdout = _stdout
70 
71     def open(self, spider):
72         return self._stdout
73 
74     def store(self, file):
75         pass
76 
77 
78 @implementer(IFeedStorage)
79 class FileFeedStorage(object):
80 
81     def __init__(self, uri):
82         self.path = file_uri_to_path(uri)
83 
84     def open(self, spider):
85         dirname = os.path.dirname(self.path)
86         if dirname and not os.path.exists(dirname):
87             os.makedirs(dirname)
88         return open(self.path, 'ab')
89 
90     def store(self, file):
91         file.close()
92 
93 
94 class S3FeedStorage(BlockingFeedStorage):
95 
96     def __init__(self, uri, access_key=None, secret_key=None, acl=None):
97         # BEGIN Backward compatibility for initialising without keys (and
98         # without using from_crawler)
99         no_defaults = access_key is None and secret_key is None
100         if no_defaults:
101             from scrapy.utils.project import get_project_settings
102             settings = get_project_settings()
103             if 'AWS_ACCESS_KEY_ID' in settings or 'AWS_SECRET_ACCESS_KEY' in settings:
104                 import warnings
105                 from scrapy.exceptions import ScrapyDeprecationWarning
106                 warnings.warn(
107                     "Initialising `scrapy.extensions.feedexport.S3FeedStorage` "
108                     "without AWS keys is deprecated. Please supply credentials or "
109                     "use the `from_crawler()` constructor.",
110                     category=ScrapyDeprecationWarning,
111                     stacklevel=2
112                 )
113                 access_key = settings['AWS_ACCESS_KEY_ID']
114                 secret_key = settings['AWS_SECRET_ACCESS_KEY']
115         # END Backward compatibility
116         u = urlparse(uri)
117         self.bucketname = u.hostname
118         self.access_key = u.username or access_key
119         self.secret_key = u.password or secret_key
120         self.is_botocore = is_botocore()
121         self.keyname = u.path[1:]  # remove first "/"
122         self.acl = acl
123         if self.is_botocore:
124             import botocore.session
125             session = botocore.session.get_session()
126             self.s3_client = session.create_client(
127                 's3', aws_access_key_id=self.access_key,
128                 aws_secret_access_key=self.secret_key)
129         else:
130             import boto
131             self.connect_s3 = boto.connect_s3
132 
133     @classmethod
134     def from_crawler(cls, crawler, uri):
135         return cls(
136             uri=uri,
137             access_key=crawler.settings['AWS_ACCESS_KEY_ID'],
138             secret_key=crawler.settings['AWS_SECRET_ACCESS_KEY'],
139             acl=crawler.settings['FEED_STORAGE_S3_ACL'] or None
140         )
141 
142     def _store_in_thread(self, file):
143         file.seek(0)
144         if self.is_botocore:
145             kwargs = {'ACL': self.acl} if self.acl else {}
146             self.s3_client.put_object(
147                 Bucket=self.bucketname, Key=self.keyname, Body=file,
148                 **kwargs)
149         else:
150             conn = self.connect_s3(self.access_key, self.secret_key)
151             bucket = conn.get_bucket(self.bucketname, validate=False)
152             key = bucket.new_key(self.keyname)
153             kwargs = {'policy': self.acl} if self.acl else {}
154             key.set_contents_from_file(file, **kwargs)
155             key.close()
156 
157 
158 class FTPFeedStorage(BlockingFeedStorage):
159 
160     def __init__(self, uri, use_active_mode=False):
161         u = urlparse(uri)
162         self.host = u.hostname
163         self.port = int(u.port or '21')
164         self.username = u.username
165         self.password = unquote(u.password)
166         self.path = u.path
167         self.use_active_mode = use_active_mode
168 
169     @classmethod
170     def from_crawler(cls, crawler, uri):
171         return cls(
172             uri=uri,
173             use_active_mode=crawler.settings.getbool('FEED_STORAGE_FTP_ACTIVE')
174         )
175 
176     def _store_in_thread(self, file):
177         file.seek(0)
178         ftp = FTP()
179         ftp.connect(self.host, self.port)
180         ftp.login(self.username, self.password)
181         if self.use_active_mode:
182             ftp.set_pasv(False)
183         dirname, filename = posixpath.split(self.path)
184         ftp_makedirs_cwd(ftp, dirname)
185         ftp.storbinary('STOR %s' % filename, file)
186         ftp.quit()
187 
188 
189 class SpiderSlot(object):
190     def __init__(self, file, exporter, storage, uri):
191         self.file = file
192         self.exporter = exporter
193         self.storage = storage
194         self.uri = uri
195         self.itemcount = 0
196 
197 
198 class FeedExporter(object):
199 
200     def __init__(self, settings):
201         self.settings = settings
202         self.urifmt = settings['FEED_URI']
203         if not self.urifmt:
204             raise NotConfigured
205         self.format = settings['FEED_FORMAT'].lower()
206         self.export_encoding = settings['FEED_EXPORT_ENCODING']
207         self.storages = self._load_components('FEED_STORAGES')
208         self.exporters = self._load_components('FEED_EXPORTERS')
209         if not self._storage_supported(self.urifmt):
210             raise NotConfigured
211         if not self._exporter_supported(self.format):
212             raise NotConfigured
213         self.store_empty = settings.getbool('FEED_STORE_EMPTY')
214         self._exporting = False
215         self.export_fields = settings.getlist('FEED_EXPORT_FIELDS') or None
216         self.indent = None
217         if settings.get('FEED_EXPORT_INDENT') is not None:
218             self.indent = settings.getint('FEED_EXPORT_INDENT')
219         uripar = settings['FEED_URI_PARAMS']
220         self._uripar = load_object(uripar) if uripar else lambda x, y: None
221 
222     @classmethod
223     def from_crawler(cls, crawler):
224         o = cls(crawler.settings)
225         o.crawler = crawler
226         crawler.signals.connect(o.open_spider, signals.spider_opened)
227         crawler.signals.connect(o.close_spider, signals.spider_closed)
228         crawler.signals.connect(o.item_scraped, signals.item_scraped)
229         return o
230 
231     def open_spider(self, spider):
232         uri = self.urifmt % self._get_uri_params(spider)
233         storage = self._get_storage(uri)
234         file = storage.open(spider)
235         exporter = self._get_exporter(file, fields_to_export=self.export_fields,
236             encoding=self.export_encoding, indent=self.indent)
237         if self.store_empty:
238             exporter.start_exporting()
239             self._exporting = True
240         self.slot = SpiderSlot(file, exporter, storage, uri)
241 
242     def close_spider(self, spider):
243         slot = self.slot
244         if not slot.itemcount and not self.store_empty:
245             # We need to call slot.storage.store nonetheless to get the file
246             # properly closed.
247             return defer.maybeDeferred(slot.storage.store, slot.file)
248         if self._exporting:
249             slot.exporter.finish_exporting()
250             self._exporting = False
251         logfmt = "%s %%(format)s feed (%%(itemcount)d items) in: %%(uri)s"
252         log_args = {'format': self.format,
253                     'itemcount': slot.itemcount,
254                     'uri': slot.uri}
255         d = defer.maybeDeferred(slot.storage.store, slot.file)
256         d.addCallback(lambda _: logger.info(logfmt % "Stored", log_args,
257                                             extra={'spider': spider}))
258         d.addErrback(lambda f: logger.error(logfmt % "Error storing", log_args,
259                                             exc_info=failure_to_exc_info(f),
260                                             extra={'spider': spider}))
261         return d
262 
263     def item_scraped(self, item, spider):
264         slot = self.slot
265         if not self._exporting:
266             slot.exporter.start_exporting()
267             self._exporting = True
268         slot.exporter.export_item(item)
269         slot.itemcount += 1
270         return item
271 
272     def _load_components(self, setting_prefix):
273         conf = without_none_values(self.settings.getwithbase(setting_prefix))
274         d = {}
275         for k, v in conf.items():
276             try:
277                 d[k] = load_object(v)
278             except NotConfigured:
279                 pass
280         return d
281 
282     def _exporter_supported(self, format):
283         if format in self.exporters:
284             return True
285         logger.error("Unknown feed format: %(format)s", {'format': format})
286 
287     def _storage_supported(self, uri):
288         scheme = urlparse(uri).scheme
289         if scheme in self.storages:
290             try:
291                 self._get_storage(uri)
292                 return True
293             except NotConfigured as e:
294                 logger.error("Disabled feed storage scheme: %(scheme)s. "
295                              "Reason: %(reason)s",
296                              {'scheme': scheme, 'reason': str(e)})
297         else:
298             logger.error("Unknown feed storage scheme: %(scheme)s",
299                          {'scheme': scheme})
300 
301     def _get_instance(self, objcls, *args, **kwargs):
302         return create_instance(
303             objcls, self.settings, getattr(self, 'crawler', None),
304             *args, **kwargs)
305 
306     def _get_exporter(self, *args, **kwargs):
307         return self._get_instance(self.exporters[self.format], *args, **kwargs)
308 
309     def _get_storage(self, uri):
310         return self._get_instance(self.storages[urlparse(uri).scheme], uri)
311 
312     def _get_uri_params(self, spider):
313         params = {}
314         for k in dir(spider):
315             params[k] = getattr(spider, k)
316         ts = datetime.utcnow().replace(microsecond=0).isoformat().replace(':', '-')
317         params['time'] = ts
318         self._uripar(params, spider)
319         return params
320
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scrapy/extensions/feedexport.py b/scrapy/extensions/feedexport.py
--- a/scrapy/extensions/feedexport.py
+++ b/scrapy/extensions/feedexport.py
@@ -199,9 +199,9 @@
 
     def __init__(self, settings):
         self.settings = settings
-        self.urifmt = settings['FEED_URI']
-        if not self.urifmt:
+        if not settings['FEED_URI']:
             raise NotConfigured
+        self.urifmt = str(settings['FEED_URI'])
         self.format = settings['FEED_FORMAT'].lower()
         self.export_encoding = settings['FEED_EXPORT_ENCODING']
         self.storages = self._load_components('FEED_STORAGES')

{"golden_diff": "diff --git a/scrapy/extensions/feedexport.py b/scrapy/extensions/feedexport.py\n--- a/scrapy/extensions/feedexport.py\n+++ b/scrapy/extensions/feedexport.py\n@@ -199,9 +199,9 @@\n \n     def __init__(self, settings):\n         self.settings = settings\n-        self.urifmt = settings['FEED_URI']\n-        if not self.urifmt:\n+        if not settings['FEED_URI']:\n             raise NotConfigured\n+        self.urifmt = str(settings['FEED_URI'])\n         self.format = settings['FEED_FORMAT'].lower()\n         self.export_encoding = settings['FEED_EXPORT_ENCODING']\n         self.storages = self._load_components('FEED_STORAGES')\n", "issue": "Support pathlib.Path in FEED_URI\nMake things work the same when the value assigned to the `FEED_URI` setting is a string containing a path or an instance of `pathlib.Path`.\n", "before_files": [{"content": "\"\"\"\nFeed Exports extension\n\nSee documentation in docs/topics/feed-exports.rst\n\"\"\"\n\nimport os\nimport sys\nimport logging\nimport posixpath\nfrom tempfile import NamedTemporaryFile\nfrom datetime import datetime\nimport six\nfrom six.moves.urllib.parse import urlparse, unquote\nfrom ftplib import FTP\n\nfrom zope.interface import Interface, implementer\nfrom twisted.internet import defer, threads\nfrom w3lib.url import file_uri_to_path\n\nfrom scrapy import signals\nfrom scrapy.utils.ftp import ftp_makedirs_cwd\nfrom scrapy.exceptions import NotConfigured\nfrom scrapy.utils.misc import create_instance, load_object\nfrom scrapy.utils.log import failure_to_exc_info\nfrom scrapy.utils.python import without_none_values\nfrom scrapy.utils.boto import is_botocore\n\nlogger = logging.getLogger(__name__)\n\n\nclass IFeedStorage(Interface):\n    \"\"\"Interface that all Feed Storages must implement\"\"\"\n\n    def __init__(uri):\n        \"\"\"Initialize the storage with the parameters given in the URI\"\"\"\n\n    def open(spider):\n        \"\"\"Open the storage for the given spider. It must return a file-like\n        object that will be used for the exporters\"\"\"\n\n    def store(file):\n        \"\"\"Store the given file stream\"\"\"\n\n\n@implementer(IFeedStorage)\nclass BlockingFeedStorage(object):\n\n    def open(self, spider):\n        path = spider.crawler.settings['FEED_TEMPDIR']\n        if path and not os.path.isdir(path):\n            raise OSError('Not a Directory: ' + str(path))\n\n        return NamedTemporaryFile(prefix='feed-', dir=path)\n\n    def store(self, file):\n        return threads.deferToThread(self._store_in_thread, file)\n\n    def _store_in_thread(self, file):\n        raise NotImplementedError\n\n\n@implementer(IFeedStorage)\nclass StdoutFeedStorage(object):\n\n    def __init__(self, uri, _stdout=None):\n        if not _stdout:\n            _stdout = sys.stdout if six.PY2 else sys.stdout.buffer\n        self._stdout = _stdout\n\n    def open(self, spider):\n        return self._stdout\n\n    def store(self, file):\n        pass\n\n\n@implementer(IFeedStorage)\nclass FileFeedStorage(object):\n\n    def __init__(self, uri):\n        self.path = file_uri_to_path(uri)\n\n    def open(self, spider):\n        dirname = os.path.dirname(self.path)\n        if dirname and not os.path.exists(dirname):\n            os.makedirs(dirname)\n        return open(self.path, 'ab')\n\n    def store(self, file):\n        file.close()\n\n\nclass S3FeedStorage(BlockingFeedStorage):\n\n    def __init__(self, uri, access_key=None, secret_key=None, acl=None):\n        # BEGIN Backward compatibility for initialising without keys (and\n        # without using from_crawler)\n        no_defaults = access_key is None and secret_key is None\n        if no_defaults:\n            from scrapy.utils.project import get_project_settings\n            settings = get_project_settings()\n            if 'AWS_ACCESS_KEY_ID' in settings or 'AWS_SECRET_ACCESS_KEY' in settings:\n                import warnings\n                from scrapy.exceptions import ScrapyDeprecationWarning\n                warnings.warn(\n                    \"Initialising `scrapy.extensions.feedexport.S3FeedStorage` \"\n                    \"without AWS keys is deprecated. Please supply credentials or \"\n                    \"use the `from_crawler()` constructor.\",\n                    category=ScrapyDeprecationWarning,\n                    stacklevel=2\n                )\n                access_key = settings['AWS_ACCESS_KEY_ID']\n                secret_key = settings['AWS_SECRET_ACCESS_KEY']\n        # END Backward compatibility\n        u = urlparse(uri)\n        self.bucketname = u.hostname\n        self.access_key = u.username or access_key\n        self.secret_key = u.password or secret_key\n        self.is_botocore = is_botocore()\n        self.keyname = u.path[1:]  # remove first \"/\"\n        self.acl = acl\n        if self.is_botocore:\n            import botocore.session\n            session = botocore.session.get_session()\n            self.s3_client = session.create_client(\n                's3', aws_access_key_id=self.access_key,\n                aws_secret_access_key=self.secret_key)\n        else:\n            import boto\n            self.connect_s3 = boto.connect_s3\n\n    @classmethod\n    def from_crawler(cls, crawler, uri):\n        return cls(\n            uri=uri,\n            access_key=crawler.settings['AWS_ACCESS_KEY_ID'],\n            secret_key=crawler.settings['AWS_SECRET_ACCESS_KEY'],\n            acl=crawler.settings['FEED_STORAGE_S3_ACL'] or None\n        )\n\n    def _store_in_thread(self, file):\n        file.seek(0)\n        if self.is_botocore:\n            kwargs = {'ACL': self.acl} if self.acl else {}\n            self.s3_client.put_object(\n                Bucket=self.bucketname, Key=self.keyname, Body=file,\n                **kwargs)\n        else:\n            conn = self.connect_s3(self.access_key, self.secret_key)\n            bucket = conn.get_bucket(self.bucketname, validate=False)\n            key = bucket.new_key(self.keyname)\n            kwargs = {'policy': self.acl} if self.acl else {}\n            key.set_contents_from_file(file, **kwargs)\n            key.close()\n\n\nclass FTPFeedStorage(BlockingFeedStorage):\n\n    def __init__(self, uri, use_active_mode=False):\n        u = urlparse(uri)\n        self.host = u.hostname\n        self.port = int(u.port or '21')\n        self.username = u.username\n        self.password = unquote(u.password)\n        self.path = u.path\n        self.use_active_mode = use_active_mode\n\n    @classmethod\n    def from_crawler(cls, crawler, uri):\n        return cls(\n            uri=uri,\n            use_active_mode=crawler.settings.getbool('FEED_STORAGE_FTP_ACTIVE')\n        )\n\n    def _store_in_thread(self, file):\n        file.seek(0)\n        ftp = FTP()\n        ftp.connect(self.host, self.port)\n        ftp.login(self.username, self.password)\n        if self.use_active_mode:\n            ftp.set_pasv(False)\n        dirname, filename = posixpath.split(self.path)\n        ftp_makedirs_cwd(ftp, dirname)\n        ftp.storbinary('STOR %s' % filename, file)\n        ftp.quit()\n\n\nclass SpiderSlot(object):\n    def __init__(self, file, exporter, storage, uri):\n        self.file = file\n        self.exporter = exporter\n        self.storage = storage\n        self.uri = uri\n        self.itemcount = 0\n\n\nclass FeedExporter(object):\n\n    def __init__(self, settings):\n        self.settings = settings\n        self.urifmt = settings['FEED_URI']\n        if not self.urifmt:\n            raise NotConfigured\n        self.format = settings['FEED_FORMAT'].lower()\n        self.export_encoding = settings['FEED_EXPORT_ENCODING']\n        self.storages = self._load_components('FEED_STORAGES')\n        self.exporters = self._load_components('FEED_EXPORTERS')\n        if not self._storage_supported(self.urifmt):\n            raise NotConfigured\n        if not self._exporter_supported(self.format):\n            raise NotConfigured\n        self.store_empty = settings.getbool('FEED_STORE_EMPTY')\n        self._exporting = False\n        self.export_fields = settings.getlist('FEED_EXPORT_FIELDS') or None\n        self.indent = None\n        if settings.get('FEED_EXPORT_INDENT') is not None:\n            self.indent = settings.getint('FEED_EXPORT_INDENT')\n        uripar = settings['FEED_URI_PARAMS']\n        self._uripar = load_object(uripar) if uripar else lambda x, y: None\n\n    @classmethod\n    def from_crawler(cls, crawler):\n        o = cls(crawler.settings)\n        o.crawler = crawler\n        crawler.signals.connect(o.open_spider, signals.spider_opened)\n        crawler.signals.connect(o.close_spider, signals.spider_closed)\n        crawler.signals.connect(o.item_scraped, signals.item_scraped)\n        return o\n\n    def open_spider(self, spider):\n        uri = self.urifmt % self._get_uri_params(spider)\n        storage = self._get_storage(uri)\n        file = storage.open(spider)\n        exporter = self._get_exporter(file, fields_to_export=self.export_fields,\n            encoding=self.export_encoding, indent=self.indent)\n        if self.store_empty:\n            exporter.start_exporting()\n            self._exporting = True\n        self.slot = SpiderSlot(file, exporter, storage, uri)\n\n    def close_spider(self, spider):\n        slot = self.slot\n        if not slot.itemcount and not self.store_empty:\n            # We need to call slot.storage.store nonetheless to get the file\n            # properly closed.\n            return defer.maybeDeferred(slot.storage.store, slot.file)\n        if self._exporting:\n            slot.exporter.finish_exporting()\n            self._exporting = False\n        logfmt = \"%s %%(format)s feed (%%(itemcount)d items) in: %%(uri)s\"\n        log_args = {'format': self.format,\n                    'itemcount': slot.itemcount,\n                    'uri': slot.uri}\n        d = defer.maybeDeferred(slot.storage.store, slot.file)\n        d.addCallback(lambda _: logger.info(logfmt % \"Stored\", log_args,\n                                            extra={'spider': spider}))\n        d.addErrback(lambda f: logger.error(logfmt % \"Error storing\", log_args,\n                                            exc_info=failure_to_exc_info(f),\n                                            extra={'spider': spider}))\n        return d\n\n    def item_scraped(self, item, spider):\n        slot = self.slot\n        if not self._exporting:\n            slot.exporter.start_exporting()\n            self._exporting = True\n        slot.exporter.export_item(item)\n        slot.itemcount += 1\n        return item\n\n    def _load_components(self, setting_prefix):\n        conf = without_none_values(self.settings.getwithbase(setting_prefix))\n        d = {}\n        for k, v in conf.items():\n            try:\n                d[k] = load_object(v)\n            except NotConfigured:\n                pass\n        return d\n\n    def _exporter_supported(self, format):\n        if format in self.exporters:\n            return True\n        logger.error(\"Unknown feed format: %(format)s\", {'format': format})\n\n    def _storage_supported(self, uri):\n        scheme = urlparse(uri).scheme\n        if scheme in self.storages:\n            try:\n                self._get_storage(uri)\n                return True\n            except NotConfigured as e:\n                logger.error(\"Disabled feed storage scheme: %(scheme)s. \"\n                             \"Reason: %(reason)s\",\n                             {'scheme': scheme, 'reason': str(e)})\n        else:\n            logger.error(\"Unknown feed storage scheme: %(scheme)s\",\n                         {'scheme': scheme})\n\n    def _get_instance(self, objcls, *args, **kwargs):\n        return create_instance(\n            objcls, self.settings, getattr(self, 'crawler', None),\n            *args, **kwargs)\n\n    def _get_exporter(self, *args, **kwargs):\n        return self._get_instance(self.exporters[self.format], *args, **kwargs)\n\n    def _get_storage(self, uri):\n        return self._get_instance(self.storages[urlparse(uri).scheme], uri)\n\n    def _get_uri_params(self, spider):\n        params = {}\n        for k in dir(spider):\n            params[k] = getattr(spider, k)\n        ts = datetime.utcnow().replace(microsecond=0).isoformat().replace(':', '-')\n        params['time'] = ts\n        self._uripar(params, spider)\n        return params\n", "path": "scrapy/extensions/feedexport.py"}], "after_files": [{"content": "\"\"\"\nFeed Exports extension\n\nSee documentation in docs/topics/feed-exports.rst\n\"\"\"\n\nimport os\nimport sys\nimport logging\nimport posixpath\nfrom tempfile import NamedTemporaryFile\nfrom datetime import datetime\nimport six\nfrom six.moves.urllib.parse import urlparse, unquote\nfrom ftplib import FTP\n\nfrom zope.interface import Interface, implementer\nfrom twisted.internet import defer, threads\nfrom w3lib.url import file_uri_to_path\n\nfrom scrapy import signals\nfrom scrapy.utils.ftp import ftp_makedirs_cwd\nfrom scrapy.exceptions import NotConfigured\nfrom scrapy.utils.misc import create_instance, load_object\nfrom scrapy.utils.log import failure_to_exc_info\nfrom scrapy.utils.python import without_none_values\nfrom scrapy.utils.boto import is_botocore\n\nlogger = logging.getLogger(__name__)\n\n\nclass IFeedStorage(Interface):\n    \"\"\"Interface that all Feed Storages must implement\"\"\"\n\n    def __init__(uri):\n        \"\"\"Initialize the storage with the parameters given in the URI\"\"\"\n\n    def open(spider):\n        \"\"\"Open the storage for the given spider. It must return a file-like\n        object that will be used for the exporters\"\"\"\n\n    def store(file):\n        \"\"\"Store the given file stream\"\"\"\n\n\n@implementer(IFeedStorage)\nclass BlockingFeedStorage(object):\n\n    def open(self, spider):\n        path = spider.crawler.settings['FEED_TEMPDIR']\n        if path and not os.path.isdir(path):\n            raise OSError('Not a Directory: ' + str(path))\n\n        return NamedTemporaryFile(prefix='feed-', dir=path)\n\n    def store(self, file):\n        return threads.deferToThread(self._store_in_thread, file)\n\n    def _store_in_thread(self, file):\n        raise NotImplementedError\n\n\n@implementer(IFeedStorage)\nclass StdoutFeedStorage(object):\n\n    def __init__(self, uri, _stdout=None):\n        if not _stdout:\n            _stdout = sys.stdout if six.PY2 else sys.stdout.buffer\n        self._stdout = _stdout\n\n    def open(self, spider):\n        return self._stdout\n\n    def store(self, file):\n        pass\n\n\n@implementer(IFeedStorage)\nclass FileFeedStorage(object):\n\n    def __init__(self, uri):\n        self.path = file_uri_to_path(uri)\n\n    def open(self, spider):\n        dirname = os.path.dirname(self.path)\n        if dirname and not os.path.exists(dirname):\n            os.makedirs(dirname)\n        return open(self.path, 'ab')\n\n    def store(self, file):\n        file.close()\n\n\nclass S3FeedStorage(BlockingFeedStorage):\n\n    def __init__(self, uri, access_key=None, secret_key=None, acl=None):\n        # BEGIN Backward compatibility for initialising without keys (and\n        # without using from_crawler)\n        no_defaults = access_key is None and secret_key is None\n        if no_defaults:\n            from scrapy.utils.project import get_project_settings\n            settings = get_project_settings()\n            if 'AWS_ACCESS_KEY_ID' in settings or 'AWS_SECRET_ACCESS_KEY' in settings:\n                import warnings\n                from scrapy.exceptions import ScrapyDeprecationWarning\n                warnings.warn(\n                    \"Initialising `scrapy.extensions.feedexport.S3FeedStorage` \"\n                    \"without AWS keys is deprecated. Please supply credentials or \"\n                    \"use the `from_crawler()` constructor.\",\n                    category=ScrapyDeprecationWarning,\n                    stacklevel=2\n                )\n                access_key = settings['AWS_ACCESS_KEY_ID']\n                secret_key = settings['AWS_SECRET_ACCESS_KEY']\n        # END Backward compatibility\n        u = urlparse(uri)\n        self.bucketname = u.hostname\n        self.access_key = u.username or access_key\n        self.secret_key = u.password or secret_key\n        self.is_botocore = is_botocore()\n        self.keyname = u.path[1:]  # remove first \"/\"\n        self.acl = acl\n        if self.is_botocore:\n            import botocore.session\n            session = botocore.session.get_session()\n            self.s3_client = session.create_client(\n                's3', aws_access_key_id=self.access_key,\n                aws_secret_access_key=self.secret_key)\n        else:\n            import boto\n            self.connect_s3 = boto.connect_s3\n\n    @classmethod\n    def from_crawler(cls, crawler, uri):\n        return cls(\n            uri=uri,\n            access_key=crawler.settings['AWS_ACCESS_KEY_ID'],\n            secret_key=crawler.settings['AWS_SECRET_ACCESS_KEY'],\n            acl=crawler.settings['FEED_STORAGE_S3_ACL'] or None\n        )\n\n    def _store_in_thread(self, file):\n        file.seek(0)\n        if self.is_botocore:\n            kwargs = {'ACL': self.acl} if self.acl else {}\n            self.s3_client.put_object(\n                Bucket=self.bucketname, Key=self.keyname, Body=file,\n                **kwargs)\n        else:\n            conn = self.connect_s3(self.access_key, self.secret_key)\n            bucket = conn.get_bucket(self.bucketname, validate=False)\n            key = bucket.new_key(self.keyname)\n            kwargs = {'policy': self.acl} if self.acl else {}\n            key.set_contents_from_file(file, **kwargs)\n            key.close()\n\n\nclass FTPFeedStorage(BlockingFeedStorage):\n\n    def __init__(self, uri, use_active_mode=False):\n        u = urlparse(uri)\n        self.host = u.hostname\n        self.port = int(u.port or '21')\n        self.username = u.username\n        self.password = unquote(u.password)\n        self.path = u.path\n        self.use_active_mode = use_active_mode\n\n    @classmethod\n    def from_crawler(cls, crawler, uri):\n        return cls(\n            uri=uri,\n            use_active_mode=crawler.settings.getbool('FEED_STORAGE_FTP_ACTIVE')\n        )\n\n    def _store_in_thread(self, file):\n        file.seek(0)\n        ftp = FTP()\n        ftp.connect(self.host, self.port)\n        ftp.login(self.username, self.password)\n        if self.use_active_mode:\n            ftp.set_pasv(False)\n        dirname, filename = posixpath.split(self.path)\n        ftp_makedirs_cwd(ftp, dirname)\n        ftp.storbinary('STOR %s' % filename, file)\n        ftp.quit()\n\n\nclass SpiderSlot(object):\n    def __init__(self, file, exporter, storage, uri):\n        self.file = file\n        self.exporter = exporter\n        self.storage = storage\n        self.uri = uri\n        self.itemcount = 0\n\n\nclass FeedExporter(object):\n\n    def __init__(self, settings):\n        self.settings = settings\n        if not settings['FEED_URI']:\n            raise NotConfigured\n        self.urifmt = str(settings['FEED_URI'])\n        self.format = settings['FEED_FORMAT'].lower()\n        self.export_encoding = settings['FEED_EXPORT_ENCODING']\n        self.storages = self._load_components('FEED_STORAGES')\n        self.exporters = self._load_components('FEED_EXPORTERS')\n        if not self._storage_supported(self.urifmt):\n            raise NotConfigured\n        if not self._exporter_supported(self.format):\n            raise NotConfigured\n        self.store_empty = settings.getbool('FEED_STORE_EMPTY')\n        self._exporting = False\n        self.export_fields = settings.getlist('FEED_EXPORT_FIELDS') or None\n        self.indent = None\n        if settings.get('FEED_EXPORT_INDENT') is not None:\n            self.indent = settings.getint('FEED_EXPORT_INDENT')\n        uripar = settings['FEED_URI_PARAMS']\n        self._uripar = load_object(uripar) if uripar else lambda x, y: None\n\n    @classmethod\n    def from_crawler(cls, crawler):\n        o = cls(crawler.settings)\n        o.crawler = crawler\n        crawler.signals.connect(o.open_spider, signals.spider_opened)\n        crawler.signals.connect(o.close_spider, signals.spider_closed)\n        crawler.signals.connect(o.item_scraped, signals.item_scraped)\n        return o\n\n    def open_spider(self, spider):\n        uri = self.urifmt % self._get_uri_params(spider)\n        storage = self._get_storage(uri)\n        file = storage.open(spider)\n        exporter = self._get_exporter(file, fields_to_export=self.export_fields,\n            encoding=self.export_encoding, indent=self.indent)\n        if self.store_empty:\n            exporter.start_exporting()\n            self._exporting = True\n        self.slot = SpiderSlot(file, exporter, storage, uri)\n\n    def close_spider(self, spider):\n        slot = self.slot\n        if not slot.itemcount and not self.store_empty:\n            return\n        if self._exporting:\n            slot.exporter.finish_exporting()\n            self._exporting = False\n        logfmt = \"%s %%(format)s feed (%%(itemcount)d items) in: %%(uri)s\"\n        log_args = {'format': self.format,\n                    'itemcount': slot.itemcount,\n                    'uri': slot.uri}\n        d = defer.maybeDeferred(slot.storage.store, slot.file)\n        d.addCallback(lambda _: logger.info(logfmt % \"Stored\", log_args,\n                                            extra={'spider': spider}))\n        d.addErrback(lambda f: logger.error(logfmt % \"Error storing\", log_args,\n                                            exc_info=failure_to_exc_info(f),\n                                            extra={'spider': spider}))\n        return d\n\n    def item_scraped(self, item, spider):\n        slot = self.slot\n        if not self._exporting:\n            slot.exporter.start_exporting()\n            self._exporting = True\n        slot.exporter.export_item(item)\n        slot.itemcount += 1\n        return item\n\n    def _load_components(self, setting_prefix):\n        conf = without_none_values(self.settings.getwithbase(setting_prefix))\n        d = {}\n        for k, v in conf.items():\n            try:\n                d[k] = load_object(v)\n            except NotConfigured:\n                pass\n        return d\n\n    def _exporter_supported(self, format):\n        if format in self.exporters:\n            return True\n        logger.error(\"Unknown feed format: %(format)s\", {'format': format})\n\n    def _storage_supported(self, uri):\n        scheme = urlparse(uri).scheme\n        if scheme in self.storages:\n            try:\n                self._get_storage(uri)\n                return True\n            except NotConfigured as e:\n                logger.error(\"Disabled feed storage scheme: %(scheme)s. \"\n                             \"Reason: %(reason)s\",\n                             {'scheme': scheme, 'reason': str(e)})\n        else:\n            logger.error(\"Unknown feed storage scheme: %(scheme)s\",\n                         {'scheme': scheme})\n\n    def _get_instance(self, objcls, *args, **kwargs):\n        return create_instance(\n            objcls, self.settings, getattr(self, 'crawler', None),\n            *args, **kwargs)\n\n    def _get_exporter(self, *args, **kwargs):\n        return self._get_instance(self.exporters[self.format], *args, **kwargs)\n\n    def _get_storage(self, uri):\n        return self._get_instance(self.storages[urlparse(uri).scheme], uri)\n\n    def _get_uri_params(self, spider):\n        params = {}\n        for k in dir(spider):\n            params[k] = getattr(spider, k)\n        ts = datetime.utcnow().replace(microsecond=0).isoformat().replace(':', '-')\n        params['time'] = ts\n        self._uripar(params, spider)\n        return params\n", "path": "scrapy/extensions/feedexport.py"}]}