Merge pull request #1826 from apache/tristan/downloadable-source
Stop passing URL opener across process boundaries
diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py
index 57c19ee..9372347 100644
--- a/src/buildstream/_stream.py
+++ b/src/buildstream/_stream.py
@@ -50,7 +50,6 @@
from .types import _KeyStrength, _PipelineSelection, _Scope, _HostMount
from .plugin import Plugin
from . import utils, node, _yaml, _site, _pipeline
-from .downloadablefilesource import DownloadableFileSource
# Stream()
@@ -118,12 +117,6 @@
# test isolation.
node._reset_global_state()
- # Ensure that any global state loaded by the downloadablefilesource
- # is discarded in between sessions (different invocations of the CLI
- # may come with different local state such as .netrc files, so we need
- # a reset here).
- DownloadableFileSource._reset_url_opener()
-
# set_project()
#
# Set the top-level project.
diff --git a/src/buildstream/downloadablefilesource.py b/src/buildstream/downloadablefilesource.py
index ce91f7c..e310335 100644
--- a/src/buildstream/downloadablefilesource.py
+++ b/src/buildstream/downloadablefilesource.py
@@ -88,7 +88,8 @@
return login, password
-def _download_file(opener, url, etag, directory):
+def _download_file(opener_creator, url, etag, directory):
+ opener = opener_creator.get_url_opener()
default_name = os.path.basename(url)
request = urllib.request.Request(url)
request.add_header("Accept", "*/*")
@@ -134,7 +135,6 @@
COMMON_CONFIG_KEYS = Source.COMMON_CONFIG_KEYS + ["url", "ref"]
- __urlopener = None
__default_mirror_file = None
def configure(self, node):
@@ -222,8 +222,10 @@
else:
etag = None
+ url_opener_creator = _UrlOpenerCreator(self._parse_netrc())
+
local_file, new_etag, error = self.blocking_activity(
- _download_file, (self.__get_urlopener(), self.url, etag, td), activity_name
+ _download_file, (url_opener_creator, self.url, etag, td), activity_name
)
if error:
@@ -246,6 +248,21 @@
self._store_etag(sha256, new_etag)
return sha256
+ def _parse_netrc(self):
+ netrc_config = None
+ try:
+ netrc_config = netrc.netrc()
+ except OSError:
+ # If the .netrc file was not found, FileNotFoundError will be
+ # raised, but OSError will be raised directly by the netrc package
+ # in the case that $HOME is not set.
+ #
+ # This will catch both cases.
+ pass
+ except netrc.NetrcParseError as e:
+ self.warn("{}: While reading .netrc: {}".format(self, e))
+ return netrc_config
+
def _get_mirror_file(self, sha=None):
if sha is not None:
return os.path.join(self._mirror_dir, sha)
@@ -255,29 +272,15 @@
return self.__default_mirror_file
- @classmethod
- def _reset_url_opener(cls):
- # Needed for tests, in order to cleanup the `netrc` configuration.
- cls.__urlopener = None # pylint: disable=unused-private-member
- def __get_urlopener(self):
- if not DownloadableFileSource.__urlopener:
- try:
- netrc_config = netrc.netrc()
- except OSError:
- # If the .netrc file was not found, FileNotFoundError will be
- # raised, but OSError will be raised directly by the netrc package
- # in the case that $HOME is not set.
- #
- # This will catch both cases.
- #
- DownloadableFileSource.__urlopener = urllib.request.build_opener()
- except netrc.NetrcParseError as e:
- self.warn("{}: While reading .netrc: {}".format(self, e))
- return urllib.request.build_opener()
- else:
- netrc_pw_mgr = _NetrcPasswordManager(netrc_config)
- http_auth = urllib.request.HTTPBasicAuthHandler(netrc_pw_mgr)
- ftp_handler = _NetrcFTPOpener(netrc_config)
- DownloadableFileSource.__urlopener = urllib.request.build_opener(http_auth, ftp_handler)
- return DownloadableFileSource.__urlopener
+class _UrlOpenerCreator:
+ def __init__(self, netrc_config):
+ self.netrc_config = netrc_config
+
+ def get_url_opener(self):
+ if self.netrc_config:
+ netrc_pw_mgr = _NetrcPasswordManager(self.netrc_config)
+ http_auth = urllib.request.HTTPBasicAuthHandler(netrc_pw_mgr)
+ ftp_handler = _NetrcFTPOpener(self.netrc_config)
+ return urllib.request.build_opener(http_auth, ftp_handler)
+ return urllib.request.build_opener()