diff --git a/qutebrowser/app.py b/qutebrowser/app.py index ae05db5c4..1e50d4a71 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -34,7 +34,7 @@ import faulthandler from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox from PyQt5.QtGui import QDesktopServices, QPixmap, QIcon from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QTimer, QUrl, - QStandardPaths, QObject, Qt) + QObject, Qt) import qutebrowser import qutebrowser.resources # pylint: disable=unused-import @@ -172,9 +172,9 @@ class Application(QApplication): readline_bridge = readline.ReadlineBridge() objreg.register('readline-bridge', readline_bridge) log.init.debug("Initializing directories...") - standarddir.init() + standarddir.init(self._args) log.init.debug("Initializing config...") - config.init(self._args) + config.init() save_manager.init_autosave() log.init.debug("Initializing web history...") history.init() @@ -223,8 +223,7 @@ class Application(QApplication): def _handle_segfault(self): """Handle a segfault from a previous run.""" - path = standarddir.get(QStandardPaths.DataLocation) - logname = os.path.join(path, 'crash.log') + logname = os.path.join(standarddir.data, 'crash.log') try: # First check if an old logfile exists. if os.path.exists(logname): @@ -248,8 +247,7 @@ class Application(QApplication): def _init_crashlogfile(self): """Start a new logfile and redirect faulthandler to it.""" - path = standarddir.get(QStandardPaths.DataLocation) - logname = os.path.join(path, 'crash.log') + logname = os.path.join(standarddir.data, 'crash.log') try: self._crashlogfile = open(logname, 'w', encoding='ascii') except OSError: diff --git a/qutebrowser/browser/adblock.py b/qutebrowser/browser/adblock.py index c2b2a3e92..e99b6ef9f 100644 --- a/qutebrowser/browser/adblock.py +++ b/qutebrowser/browser/adblock.py @@ -25,7 +25,7 @@ import functools import posixpath import zipfile -from PyQt5.QtCore import QStandardPaths, QTimer +from PyQt5.QtCore import QTimer from qutebrowser.config import config from qutebrowser.utils import objreg, standarddir, log, message @@ -92,8 +92,7 @@ class HostBlocker: self.blocked_hosts = set() self._in_progress = [] self._done_count = 0 - data_dir = standarddir.get(QStandardPaths.DataLocation) - self._hosts_file = os.path.join(data_dir, 'blocked-hosts') + self._hosts_file = os.path.join(standarddir.data, 'blocked-hosts') objreg.get('config').changed.connect(self.on_config_changed) def read_hosts(self): diff --git a/qutebrowser/browser/cache.py b/qutebrowser/browser/cache.py index e77f86ca6..52945660f 100644 --- a/qutebrowser/browser/cache.py +++ b/qutebrowser/browser/cache.py @@ -21,7 +21,6 @@ import os.path -from PyQt5.QtCore import QStandardPaths from PyQt5.QtNetwork import QNetworkDiskCache, QNetworkCacheMetaData from qutebrowser.config import config @@ -34,8 +33,7 @@ class DiskCache(QNetworkDiskCache): def __init__(self, parent=None): super().__init__(parent) - cache_dir = standarddir.get(QStandardPaths.CacheLocation) - self.setCacheDirectory(os.path.join(cache_dir, 'http')) + self.setCacheDirectory(os.path.join(standarddir.cache, 'http')) self.setMaximumCacheSize(config.get('storage', 'cache-size')) objreg.get('config').changed.connect(self.cache_size_changed) diff --git a/qutebrowser/browser/cookies.py b/qutebrowser/browser/cookies.py index 679617c4f..9538c556b 100644 --- a/qutebrowser/browser/cookies.py +++ b/qutebrowser/browser/cookies.py @@ -20,7 +20,7 @@ """Handling of HTTP cookies.""" from PyQt5.QtNetwork import QNetworkCookie, QNetworkCookieJar -from PyQt5.QtCore import pyqtSignal, QStandardPaths, QDateTime +from PyQt5.QtCore import pyqtSignal, QDateTime from qutebrowser.config import config from qutebrowser.config.parsers import line as lineparser @@ -70,8 +70,7 @@ class CookieJar(RAMCookieJar): def __init__(self, parent=None): super().__init__(parent) - datadir = standarddir.get(QStandardPaths.DataLocation) - self._linecp = lineparser.LineConfigParser(datadir, 'cookies', + self._linecp = lineparser.LineConfigParser(standarddir.data, 'cookies', binary=True, parent=self) cookies = [] for line in self._linecp: diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index dcbd44cf6..71687e702 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -28,8 +28,7 @@ import functools import collections from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QObject, QTimer, - QStandardPaths, Qt, QVariant, QAbstractListModel, - QModelIndex, QUrl) + Qt, QVariant, QAbstractListModel, QModelIndex, QUrl) from PyQt5.QtGui import QDesktopServices from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply # We need this import so PyQt can use it inside pyqtSlot @@ -414,8 +413,7 @@ class DownloadItem(QObject): # save it under that filename in the default directory. download_dir = config.get('storage', 'download-directory') if download_dir is None: - download_dir = standarddir.get( - QStandardPaths.DownloadLocation) + download_dir = standarddir.download self._filename = os.path.join(download_dir, filename) self.basename = filename log.downloads.debug("Setting filename to {}".format(filename)) diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index 6ed9d9626..f11c647b8 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -22,7 +22,7 @@ import time import functools -from PyQt5.QtCore import pyqtSignal, QStandardPaths +from PyQt5.QtCore import pyqtSignal from PyQt5.QtWebKit import QWebHistoryInterface from qutebrowser.utils import utils, objreg, standarddir @@ -64,8 +64,7 @@ class WebHistory(QWebHistoryInterface): def __init__(self, parent=None): super().__init__(parent) - datadir = standarddir.get(QStandardPaths.DataLocation) - self._linecp = lineparser.LineConfigParser(datadir, 'history', + self._linecp = lineparser.LineConfigParser(standarddir.data, 'history', parent=self) self._history = [HistoryEntry.from_str(e) for e in self._linecp.data] objreg.get('save-manager').add_saveable('history', self.save, diff --git a/qutebrowser/browser/quickmarks.py b/qutebrowser/browser/quickmarks.py index 52387456d..389aee7dd 100644 --- a/qutebrowser/browser/quickmarks.py +++ b/qutebrowser/browser/quickmarks.py @@ -28,7 +28,7 @@ import os.path import functools import collections -from PyQt5.QtCore import pyqtSignal, QStandardPaths, QUrl, QObject +from PyQt5.QtCore import pyqtSignal, QUrl, QObject from qutebrowser.utils import message, usertypes, urlutils, standarddir, objreg from qutebrowser.commands import cmdexc, cmdutils @@ -52,9 +52,8 @@ class QuickmarkManager(QObject): self.marks = collections.OrderedDict() - confdir = standarddir.get(QStandardPaths.ConfigLocation) - self._linecp = lineparser.LineConfigParser(confdir, 'quickmarks', - parent=self) + self._linecp = lineparser.LineConfigParser( + standarddir.config, 'quickmarks', parent=self) for line in self._linecp: try: key, url = line.rsplit(maxsplit=1) @@ -62,7 +61,7 @@ class QuickmarkManager(QObject): message.error(0, "Invalid quickmark '{}'".format(line)) else: self.marks[key] = url - filename = os.path.join(confdir, 'quickmarks') + filename = os.path.join(standarddir.config, 'quickmarks') objreg.get('save-manager').add_saveable('quickmark-manager', self.save, self.changed, filename=filename) diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index b978e3d05..bac2f64fc 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -23,8 +23,8 @@ import os import os.path import tempfile -from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QObject, QStandardPaths, - QSocketNotifier, QProcessEnvironment, QProcess) +from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QObject, QSocketNotifier, + QProcessEnvironment, QProcess) from qutebrowser.utils import message, log, objreg, standarddir from qutebrowser.commands import runners, cmdexc @@ -178,7 +178,6 @@ class _POSIXUserscriptRunner(_BaseUserscriptRunner): self._reader = None def run(self, cmd, *args, env=None): - rundir = standarddir.get(QStandardPaths.RuntimeLocation) try: # tempfile.mktemp is deprecated and discouraged, but we use it here # to create a FIFO since the only other alternative would be to @@ -186,7 +185,7 @@ class _POSIXUserscriptRunner(_BaseUserscriptRunner): # os.mkfifo will raise an exception anyways when the path doesn't # exist, it shouldn't be a big issue. self._filepath = tempfile.mktemp(prefix='qutebrowser-userscript-', - dir=rundir) + dir=standarddir.runtime) os.mkfifo(self._filepath) # pylint: disable=no-member except OSError as e: message.error(self._win_id, "Error while creating FIFO: {}".format( diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index ee6bcb08a..b7521e712 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -32,8 +32,7 @@ import configparser import collections import collections.abc -from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QObject, QStandardPaths, QUrl, - QSettings) +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl, QSettings from PyQt5.QtWidgets import QMessageBox from qutebrowser.config import configdata, configexc, textwrapper @@ -114,16 +113,11 @@ def section(sect): return objreg.get('config')[sect] -def _init_main_config(args): - """Initialize the main config. - - Args: - args: The argparse namespace. - """ - confdir = standarddir.get(QStandardPaths.ConfigLocation, args) +def _init_main_config(): + """Initialize the main config.""" try: app = objreg.get('app') - config_obj = ConfigManager(confdir, 'qutebrowser.conf', app) + config_obj = ConfigManager(standarddir.config, 'qutebrowser.conf', app) except (configexc.Error, configparser.Error, UnicodeDecodeError) as e: log.init.exception(e) errstr = "Error while reading config:" @@ -140,8 +134,8 @@ def _init_main_config(args): sys.exit(1) else: objreg.register('config', config_obj) - if confdir is not None: - filename = os.path.join(confdir, 'qutebrowser.conf') + if standarddir.config is not None: + filename = os.path.join(standarddir.config, 'qutebrowser.conf') save_manager = objreg.get('save-manager') save_manager.add_saveable( 'config', config_obj.save, config_obj.changed, @@ -155,15 +149,10 @@ def _init_main_config(args): return -def _init_key_config(args): - """Initialize the key config. - - Args: - args: The argparse namespace. - """ - confdir = standarddir.get(QStandardPaths.ConfigLocation, args) +def _init_key_config(): + """Initialize the key config.""" try: - key_config = keyconf.KeyConfigParser(confdir, 'keys.conf') + key_config = keyconf.KeyConfigParser(standarddir.config, 'keys.conf') except (keyconf.KeyConfigError, UnicodeDecodeError) as e: log.init.exception(e) errstr = "Error while reading key config:\n" @@ -177,29 +166,24 @@ def _init_key_config(args): sys.exit(1) else: objreg.register('key-config', key_config) - if confdir is not None: + if standarddir.config is not None: save_manager = objreg.get('save-manager') - filename = os.path.join(confdir, 'keys.conf') + filename = os.path.join(standarddir.config, 'keys.conf') save_manager.add_saveable( 'key-config', key_config.save, key_config.changed, config_opt=('general', 'auto-save-config'), filename=filename) -def _init_misc(args): - """Initialize misc. config-related files. - - Args: - args: The argparse namespace. - """ +def _init_misc(): + """Initialize misc. config-related files.""" save_manager = objreg.get('save-manager') - datadir = standarddir.get(QStandardPaths.DataLocation, args) - state_config = ini.ReadWriteConfigParser(datadir, 'state') + state_config = ini.ReadWriteConfigParser(standarddir.data, 'state') objreg.register('state-config', state_config) save_manager.add_saveable('state-config', state_config.save) # We need to import this here because lineparser needs config. from qutebrowser.config.parsers import line - command_history = line.LineConfigParser(datadir, 'cmd-history', + command_history = line.LineConfigParser(standarddir.data, 'cmd-history', ('completion', 'history-length'), parent=objreg.get('config')) objreg.register('command-history', command_history) @@ -213,24 +197,19 @@ def _init_misc(args): # This fixes one of the corruption issues here: # https://github.com/The-Compiler/qutebrowser/issues/515 - config_path = standarddir.get(QStandardPaths.ConfigLocation, args) - if config_path is None: + if standarddir.config is None: path = os.devnull else: - path = os.path.join(config_path, 'qsettings') + path = os.path.join(standarddir.config, 'qsettings') for fmt in (QSettings.NativeFormat, QSettings.IniFormat): QSettings.setPath(fmt, QSettings.UserScope, path) -def init(args): - """Initialize the config. - - Args: - args: The argparse namespace. - """ - _init_main_config(args) - _init_key_config(args) - _init_misc(args) +def init(): + """Initialize the config.""" + _init_main_config() + _init_key_config() + _init_misc() class ConfigManager(QObject): diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py index 131025bea..eddbe9ef3 100644 --- a/qutebrowser/config/websettings.py +++ b/qutebrowser/config/websettings.py @@ -29,7 +29,7 @@ Module attributes: import os.path from PyQt5.QtWebKit import QWebSettings -from PyQt5.QtCore import QStandardPaths, QUrl +from PyQt5.QtCore import QUrl from qutebrowser.config import config from qutebrowser.utils import usertypes, standarddir, objreg @@ -194,15 +194,13 @@ def _set_setting(typ, arg, default=UNSET, value=UNSET): def init(): """Initialize the global QWebSettings.""" - cachedir = standarddir.get(QStandardPaths.CacheLocation) - QWebSettings.setIconDatabasePath(cachedir) + QWebSettings.setIconDatabasePath(standarddir.cache) QWebSettings.setOfflineWebApplicationCachePath( - os.path.join(cachedir, 'application-cache')) - datadir = standarddir.get(QStandardPaths.DataLocation) + os.path.join(standarddir.cache, 'application-cache')) QWebSettings.globalSettings().setLocalStoragePath( - os.path.join(datadir, 'local-storage')) + os.path.join(standarddir.data, 'local-storage')) QWebSettings.setOfflineStoragePath( - os.path.join(datadir, 'offline-storage')) + os.path.join(standarddir.data, 'offline-storage')) global settings settings = QWebSettings.globalSettings() diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index 3481a8dc7..8a1137250 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -22,8 +22,7 @@ import os import os.path -from PyQt5.QtCore import (pyqtSignal, QStandardPaths, QUrl, QObject, QPoint, - QTimer) +from PyQt5.QtCore import pyqtSignal, QUrl, QObject, QPoint, QTimer from PyQt5.QtWidgets import QApplication import yaml try: @@ -65,8 +64,7 @@ class SessionManager(QObject): def __init__(self, parent=None): super().__init__(parent) - self._base_path = os.path.join( - standarddir.get(QStandardPaths.DataLocation), 'sessions') + self._base_path = os.path.join(standarddir.data, 'sessions') self._last_window_session = None if not os.path.exists(self._base_path): os.mkdir(self._base_path) diff --git a/qutebrowser/test/config/test_config.py b/qutebrowser/test/config/test_config.py index 1c76f6209..06ab02983 100644 --- a/qutebrowser/test/config/test_config.py +++ b/qutebrowser/test/config/test_config.py @@ -34,7 +34,7 @@ from PyQt5.QtGui import QColor from qutebrowser.config import config, configexc from qutebrowser.test import helpers -from qutebrowser.utils import objreg +from qutebrowser.utils import objreg, standarddir class ConfigParserTests(unittest.TestCase): @@ -184,7 +184,8 @@ class ConfigInitTests(unittest.TestCase): """Test initializing with config path set to None.""" args = types.SimpleNamespace(confdir='') with helpers.environ_set_temp(self.env): - config.init(args) + standarddir.init(args) + config.init() self.assertFalse(os.listdir(self.conf_path)) diff --git a/qutebrowser/test/utils/test_standarddir.py b/qutebrowser/test/utils/test_standarddir.py index 477b56e82..ff34760ca 100644 --- a/qutebrowser/test/utils/test_standarddir.py +++ b/qutebrowser/test/utils/test_standarddir.py @@ -26,8 +26,6 @@ import shutil import unittest import tempfile -from PyQt5.QtCore import QStandardPaths - from qutebrowser.utils import standarddir from qutebrowser.test import helpers, qApp @@ -50,53 +48,53 @@ class GetStandardDirLinuxTests(unittest.TestCase): def test_data_explicit(self): """Test data dir with XDG_DATA_HOME explicitely set.""" with helpers.environ_set_temp({'XDG_DATA_HOME': self.temp_dir}): - cur_dir = standarddir.get(QStandardPaths.DataLocation) - self.assertEqual(cur_dir, os.path.join(self.temp_dir, - 'qutebrowser')) + standarddir.init(None) + expected = os.path.join(self.temp_dir, 'qutebrowser') + self.assertEqual(standarddir.data, expected) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") def test_config_explicit(self): """Test config dir with XDG_CONFIG_HOME explicitely set.""" with helpers.environ_set_temp({'XDG_CONFIG_HOME': self.temp_dir}): - cur_dir = standarddir.get(QStandardPaths.ConfigLocation) - self.assertEqual(cur_dir, os.path.join(self.temp_dir, - 'qutebrowser')) + standarddir.init(None) + expected = os.path.join(self.temp_dir, 'qutebrowser') + self.assertEqual(standarddir.config, expected) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") def test_cache_explicit(self): """Test cache dir with XDG_CACHE_HOME explicitely set.""" with helpers.environ_set_temp({'XDG_CACHE_HOME': self.temp_dir}): - cur_dir = standarddir.get(QStandardPaths.CacheLocation) - self.assertEqual(cur_dir, os.path.join(self.temp_dir, - 'qutebrowser')) + standarddir.init(None) + expected = os.path.join(self.temp_dir, 'qutebrowser') + self.assertEqual(standarddir.cache, expected) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") def test_data(self): """Test data dir with XDG_DATA_HOME not set.""" env = {'HOME': self.temp_dir, 'XDG_DATA_HOME': None} with helpers.environ_set_temp(env): - cur_dir = standarddir.get(QStandardPaths.DataLocation) - self.assertEqual(cur_dir, os.path.join(self.temp_dir, '.local', - 'share', 'qutebrowser')) + standarddir.init(None) + expected = os.path.join(self.temp_dir, '.local', 'share', + 'qutebrowser') + self.assertEqual(standarddir.data, expected) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") def test_config(self): """Test config dir with XDG_CONFIG_HOME not set.""" env = {'HOME': self.temp_dir, 'XDG_CONFIG_HOME': None} with helpers.environ_set_temp(env): - cur_dir = standarddir.get( - QStandardPaths.ConfigLocation) - self.assertEqual(cur_dir, os.path.join(self.temp_dir, '.config', - 'qutebrowser')) + standarddir.init(None) + expected = os.path.join(self.temp_dir, '.config', 'qutebrowser') + self.assertEqual(standarddir.config, expected) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") def test_cache(self): """Test cache dir with XDG_CACHE_HOME not set.""" env = {'HOME': self.temp_dir, 'XDG_CACHE_HOME': None} with helpers.environ_set_temp(env): - cur_dir = standarddir.get(QStandardPaths.CacheLocation) - self.assertEqual(cur_dir, os.path.join(self.temp_dir, '.cache', - 'qutebrowser')) + standarddir.init(None) + expected = os.path.join(self.temp_dir, '.cache', 'qutebrowser') + self.assertEqual(standarddir.cache, expected) def tearDown(self): qApp.setApplicationName(self.old_name) @@ -115,6 +113,7 @@ class GetStandardDirWindowsTests(unittest.TestCase): self.old_name = qApp.applicationName() # We can't store the files in a temp dir, so we don't chose qutebrowser qApp.setApplicationName('qutebrowser_test') + standarddir.init(None) def tearDown(self): qApp.setApplicationName(self.old_name) @@ -122,20 +121,18 @@ class GetStandardDirWindowsTests(unittest.TestCase): @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_data(self): """Test data dir.""" - cur_dir = standarddir.get(QStandardPaths.DataLocation) - self.assertEqual(cur_dir.split(os.sep)[-2:], - ['qutebrowser_test', 'data'], cur_dir) + self.assertEqual(standarddir.data.split(os.sep)[-2:], + ['qutebrowser_test', 'data'], standarddir.data) @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_config(self): """Test config dir.""" - cur_dir = standarddir.get(QStandardPaths.ConfigLocation) - self.assertEqual(cur_dir.split(os.sep)[-1], 'qutebrowser_test', - cur_dir) + self.assertEqual(standarddir.config.split(os.sep)[-1], + 'qutebrowser_test', + standarddir.config) @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_cache(self): """Test cache dir.""" - cur_dir = standarddir.get(QStandardPaths.CacheLocation) - self.assertEqual(cur_dir.split(os.sep)[-2:], - ['qutebrowser_test', 'cache'], cur_dir) + self.assertEqual(standarddir.cache.split(os.sep)[-2:], + ['qutebrowser_test', 'cache'], standarddir.cache) diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py index 96fbd858f..51a522e38 100644 --- a/qutebrowser/utils/standarddir.py +++ b/qutebrowser/utils/standarddir.py @@ -27,6 +27,13 @@ from PyQt5.QtCore import QCoreApplication, QStandardPaths from qutebrowser.utils import log, qtutils +config = None +data = None +cache = None +download = None +runtime = None + + def _writable_location(typ): """Wrapper around QStandardPaths.writableLocation.""" with qtutils.unset_organization(): @@ -68,7 +75,7 @@ def _from_args(typ, args): return (True, arg_value) -def get(typ, args=None): +def _get(typ, args=None): """Get the directory where files of the given type should be written to. Args: @@ -99,11 +106,14 @@ def get(typ, args=None): return path -def init(): +def init(args): """Initialize all standard dirs.""" - config_dir = get(QStandardPaths.ConfigLocation) - data_dir = get(QStandardPaths.DataLocation) - cache_dir = get(QStandardPaths.CacheLocation) + global config, data, cache, download, runtime + config = _get(QStandardPaths.ConfigLocation, args) + data = _get(QStandardPaths.DataLocation, args) + cache = _get(QStandardPaths.CacheLocation, args) + download = _get(QStandardPaths.DownloadLocation, args) + runtime = _get(QStandardPaths.RuntimeLocation, args) # From the XDG basedir spec: # If, when attempting to write a file, the destination directory is # non-existant an attempt should be made to create it with permission @@ -113,11 +123,11 @@ def init(): # This is slightly wrong according to the standard as we ensure these paths # exists while initializing, not when writing the file - but practicality # beats purity. - for path in (config_dir, data_dir, cache_dir): + for path in (config, data, cache): if path is not None and not os.path.exists(path): os.makedirs(path, 0o700) # http://www.brynosaurus.com/cachedir/spec.html - cachedir_tag = os.path.join(cache_dir, 'CACHEDIR.TAG') + cachedir_tag = os.path.join(cache, 'CACHEDIR.TAG') if not os.path.exists(cachedir_tag): try: with open(cachedir_tag, 'w', encoding='utf-8') as f: