Just repeat the installation procedure for the new source package. If
your config file and data file were in the old source directory, move
them over to the new source directory. If the config and data files
-were in another directory (e.g. ``~/.config/rss2email/``), there is no
-need to move them.
+were in another directory (e.g. ``~/.config`` and ``~/.local/share``),
+there is no need to move them.
Using rss2email
===============
$ r2e new you@yourdomain.com
This command will create a configuration file
-(``~/.config/rss2email/config`` by default) and a feed database
-(``~/.config/rss2email/feeds.dat`` by default). If you'd rather those
+(``$XDG_CONFIG_HOME/rss2email.cfs`` by default) and a feed database
+(``$XDG_DATA_HOME/rss2email.json`` by default). If you'd rather those
files were stored in other locations, use the ``--config`` and
-``--data`` options.
+``--data`` options. ``XDG_CONFIG_HOME`` defaults to ``$HOME/.config``
+and ``XDG_DATA_HOME`` defaults to ``$HOME/.local/share``.
You should edit the default configuration file now to adjust rss2email
for your local system. Unless you've installed a local
Print the rss2email version and exit.
.TP
\-c, \-\-config \fI<path>\fR
-The program is configured by ~/\&.config/rss2mail/config by
-default. Use this option to set a different config file.
+The program configuration is read from $XDG_CONFIG_HOME/rss2mail.cfg
+by default (see also FILES and ENVIRONMENT VARIABLES below). Use this
+option to set a different configuration file.
.TP
\-d, \-\-data \fI<path>\fR
-The program is configured by ~/\&.config/rss2mail/config by
-default. Use this option to set a different config file.
+Dynamic program data is read from $XDG_DATA_HOME/rss2mail\&.json by
+default (see also FILES and ENVIRONMENT VARIABLES below). Use this
+option to set a different data file.
.TP
\-V, \-\-verbose
Increment the logging verbosity.
the data to stdout.
.SH "CONFIGURATION"
The program's behavior can be controlled via the
-~/.config/rss2email/config file. The file format is similar to a
-Microsoft Windows INI file. It is parsed by Python's ConfigParser
-class, so see the Python documentation at
+$XDG_CONFIG_HOME/rss2email.cfg (see also FILES and ENVIRONMENT
+VARIABLES below). The file format is similar to a Microsoft Windows
+INI file. It is parsed by Python's ConfigParser class, so see the
+Python documentation at
http://docs\&.python\&.org/py3k/library/configparser\&.html for format
details.
.P
.RE
.P
.SH FILES
+.TP 4
+.B $XDG_CONFIG_HOME/rss2email.cfg
+If this file exists, it it read to configure the program.
.TP
-.B ~/.rss2email/feeds\&.dat
+.B $XDG_DATA_HOME/rss2email\&.json
The database of feeds. Use \fBr2e\fR to add, remove, or modify feeds,
do not edit it directly.
-.TP
-.B ~/.rss2email/config\&.py
-If this file exists, it it read to configure the program.
+.SH "ENVIRONMENT VARIABLES"
+The environment variables used by \fBr2e\fR are all defined in the XDG
+Base Directory Specification, which aims to standardize locations for
+user-specific configuration and data files.
+.TP 4
+.B XDG_CONFIG_HOME
+The preferred directory for configuration files. Defaults to
+$HOME/\&.config.
+.TP
+.B XDG_DATA_HOME
+The preferred directory for data files. Defaults to
+$HOME/\&.local/share.
+.TP
+.B XDG_CONFIG_DIRS
+A colon ':' separated, preference ordered list of base directories for
+configuration files in addition to $XDG_CONFIG_HOME. Defaults to
+/etc/xdg. If multiple configuration files are found in this path,
+they will all be read by the ConfigParser class (see also
+CONFIGURATION above).
+.TP
+.B XDG_DATA_DIRS
+A colon ':' separated, preference ordered list of base directories for
+data files. Defaults to /usr/local/share/:/usr/share/. Only the
+first matching file is used.
+.B
.SH AUTHORS
rss2email was started by Aaron Swartz, and is currently maintained by
Lindsey Smith. For a more complete list of contributors, see the
pass
+# Path to the filesystem root, '/' on POSIX.1 (IEEE Std 1003.1-2008).
+ROOT_PATH = _os.path.splitdrive(_sys.executable)[0] or _os.sep
+
+
class Feeds (list):
"""Utility class for rss2email activity.
Setup a temporary directory to load.
>>> tmpdir = tempfile.TemporaryDirectory(prefix='rss2email-test-')
- >>> configfile = os.path.join(tmpdir.name, 'config')
+ >>> configfile = os.path.join(tmpdir.name, 'rss2email.cfg')
>>> with open(configfile, 'w') as f:
... count = f.write('[DEFAULT]\\n')
... count = f.write('to = a@b.com\\n')
... count = f.write('to = x@y.net\\n')
... count = f.write('[feed.f2]\\n')
... count = f.write('url = http://b.com/rss.atom\\n')
- >>> datafile = os.path.join(tmpdir.name, 'feeds.dat')
+ >>> datafile = os.path.join(tmpdir.name, 'rss2email.json')
>>> with codecs.open(datafile, 'w', Feeds.datafile_encoding) as f:
... json.dump({
... 'version': 1,
datafile_version = 1
datafile_encoding = 'utf-8'
- def __init__(self, configdir=None, datafile=None, configfiles=None,
- config=None):
+ def __init__(self, configfiles=None, datafile=None, config=None):
super(Feeds, self).__init__()
- if configdir is None:
- configdir = _os.path.expanduser(_os.path.join(
- '~', '.config', 'rss2email'))
- if datafile is None:
- datafile = _os.path.join(configdir, 'feeds.dat')
- self.datafile = datafile
if configfiles is None:
- configfiles = [_os.path.join(configdir, 'config')]
+ configfiles = self._get_configfiles()
self.configfiles = configfiles
+ if datafile is None:
+ datafile = self._get_datafile()
+ self.datafile = datafile
if config is None:
config = _config.CONFIG
self.config = config
while self:
self.pop(0)
+ def _get_configfiles(self):
+ """Get configuration file paths
+
+ Following the XDG Base Directory Specification.
+ """
+ config_home = _os.environ.get(
+ 'XDG_CONFIG_HOME',
+ _os.path.expanduser(_os.path.join('~', '.config')))
+ config_dirs = [config_home]
+ config_dirs.extend(
+ _os.environ.get(
+ 'XDG_CONFIG_DIRS',
+ _os.path.join(ROOT_PATH, 'etc', 'xdg'),
+ ).split(':'))
+ # reverse because ConfigParser wants most significan last
+ return list(reversed(
+ [_os.path.join(config_dir, 'rss2email.cfg')
+ for config_dir in config_dirs]))
+
+ def _get_datafile(self):
+ """Get the data file path
+
+ Following the XDG Base Directory Specification.
+ """
+ data_home = _os.environ.get(
+ 'XDG_DATA_HOME',
+ _os.path.expanduser(_os.path.join('~', '.local', 'share')))
+ data_dirs = [data_home]
+ data_dirs.extend(
+ _os.environ.get(
+ 'XDG_DATA_DIRS',
+ ':'.join([
+ _os.path.join(ROOT_PATH, 'usr', 'local', 'share'),
+ _os.path.join(ROOT_PATH, 'usr', 'share'),
+ ]),
+ ).split(':'))
+ datafiles = [_os.path.join(data_dir, 'rss2email.json')
+ for data_dir in data_dirs]
+ for datafile in datafiles:
+ if _os.path.isfile(datafile):
+ return datafile
+ return datafiles[0]
+
def load(self, lock=True, require=False):
_LOG.debug('load feed configuration from {}'.format(self.configfiles))
if self.configfiles:
self.read_configfiles = self.config.read(self.configfiles)
else:
self.read_configfiles = []
- _LOG.debug('loaded confguration from {}'.format(self.read_configfiles))
+ _LOG.debug('loaded configuration from {}'.format(
+ self.read_configfiles))
self._load_feeds(lock=lock, require=require)
def _load_feeds(self, lock, require):
raise _error.NoDataFile(feeds=self)
_LOG.info('feed data file not found at {}'.format(self.datafile))
_LOG.debug('creating an empty data file')
+ dirname = _os.path.dirname(self.datafile)
+ if dirname and not _os.path.isdir(dirname):
+ _os.makedirs(dirname, mode=0o700, exist_ok=True)
with _codecs.open(self.datafile, 'w', self.datafile_encoding) as f:
self._save_feed_states(feeds=[], stream=f)
try:
feed.save_to_config()
dirname = _os.path.dirname(self.configfiles[-1])
if dirname and not _os.path.isdir(dirname):
- _os.makedirs(dirname)
+ _os.makedirs(dirname, mode=0o700, exist_ok=True)
with open(self.configfiles[-1], 'w') as f:
self.config.write(f)
self._save_feeds()
_LOG.debug('save feed data to {}'.format(self.datafile))
dirname = _os.path.dirname(self.datafile)
if dirname and not _os.path.isdir(dirname):
- _os.makedirs(dirname)
+ _os.makedirs(dirname, mode=0o700, exist_ok=True)
if UNIX:
tmpfile = self.datafile + '.tmp'
with _codecs.open(tmpfile, 'w', self.datafile_encoding) as f: