diff --git a/.travis.yml b/.travis.yml
index f65a0f9..a019d9b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,7 +34,7 @@ matrix:
install: pip install -U tox-travis
-script: tox
+script: tox -- -v
after_success:
- pip install codecov
diff --git a/LICENSE b/LICENSE
index 7a86ca3..8fcff22 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
The MIT License (MIT)
-Copyright (c) 2009-2018 - Simon Conseil
+Copyright (c) 2009-2020 - Simon Conseil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
diff --git a/docs/changelog.py b/docs/changelog.py
index 6742201..d1f66c4 100644
--- a/docs/changelog.py
+++ b/docs/changelog.py
@@ -51,7 +51,7 @@ A total of %d pull requests were merged for this release.
def get_authors(revision_range):
- pat = u'^.*\\t(.*)$'
+ pat = '^.*\\t(.*)$'
lst_release, cur_release = [r.strip() for r in revision_range.split('..')]
# authors, in current release and previous to current release.
@@ -61,7 +61,7 @@ def get_authors(revision_range):
re.M))
# Append '+' to new authors.
- authors = [s + u' +' for s in cur - pre] + [s for s in cur & pre]
+ authors = [s + ' +' for s in cur - pre] + [s for s in cur & pre]
authors.sort()
return authors
@@ -72,17 +72,17 @@ def get_pull_requests(repo, revision_range):
# From regular merges
merges = this_repo.git.log(
'--oneline', '--merges', revision_range)
- issues = re.findall(u"Merge pull request \\#(\\d*)", merges)
+ issues = re.findall("Merge pull request \\#(\\d*)", merges)
prnums.extend(int(s) for s in issues)
# From Homu merges (Auto merges)
- issues = re. findall(u"Auto merge of \\#(\\d*)", merges)
+ issues = re. findall("Auto merge of \\#(\\d*)", merges)
prnums.extend(int(s) for s in issues)
# From fast forward squash-merges
commits = this_repo.git.log(
'--oneline', '--no-merges', '--first-parent', revision_range)
- issues = re.findall(u'^.*\\(\\#(\\d+)\\)$', commits, re.M)
+ issues = re.findall('^.*\\(\\#(\\d+)\\)$', commits, re.M)
prnums.extend(int(s) for s in issues)
# get PR data from github repo
@@ -99,19 +99,19 @@ def main(token, revision_range):
# document authors
authors = get_authors(revision_range)
- heading = u"Contributors"
+ heading = "Contributors"
print()
print(heading)
print("=" * len(heading))
print(author_msg % len(authors))
for s in authors:
- print(u'* ' + s)
+ print('* ' + s)
# document pull requests
pull_requests = get_pull_requests(github_repo, revision_range)
- heading = u"Pull requests merged"
- pull_msg = u"* `#{0} <{1}>`__: {2}"
+ heading = "Pull requests merged"
+ pull_msg = "* `#{0} <{1}>`__: {2}"
print()
print(heading)
@@ -119,11 +119,11 @@ def main(token, revision_range):
print(pull_request_msg % len(pull_requests))
for pull in pull_requests:
- title = re.sub(u"\\s+", u" ", pull.title.strip())
+ title = re.sub("\\s+", " ", pull.title.strip())
if len(title) > 60:
- remainder = re.sub(u"\\s.*$", u"...", title[60:])
+ remainder = re.sub("\\s.*$", "...", title[60:])
if len(remainder) > 20:
- remainder = title[:80] + u"..."
+ remainder = title[:80] + "..."
else:
title = title[:60] + remainder
print(pull_msg.format(pull.number, pull.html_url, title))
diff --git a/docs/conf.py b/docs/conf.py
index dfa0ca5..965c465 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -38,7 +38,7 @@ master_doc = 'index'
# General information about the project.
project = 'Sigal'
-copyright = '2012-2019, Simon Conseil'
+copyright = '2012-2020, Simon Conseil'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
diff --git a/docs/contribute.rst b/docs/contribute.rst
index cb66e26..db29c2d 100644
--- a/docs/contribute.rst
+++ b/docs/contribute.rst
@@ -21,7 +21,7 @@ Install sigal in development mode::
Install additional dependencies for development (Sphinx, pytest), and optional
dependencies::
- pip install -r requirements.txt
+ pip install -e .\[all,tests\]
Building the docs
-----------------
diff --git a/docs/installation.rst b/docs/installation.rst
index e089871..8342786 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -8,7 +8,7 @@ With pip::
Or to install with optional dependencies (listed below)::
- $ pip install sigal\[all\]
+ $ pip install sigal\[all,tests\]
To install the development version, see the :doc:`contribute`.
@@ -29,8 +29,8 @@ The mandatory dependencies are:
- Pillow
- Python Markdown
-There are also a number of optional dependencies for the :doc:`plugins`, listed
-in the ``requirements.txt`` file:
+There are also a number of optional dependencies for the :doc:`plugins`,
+installable with the ``[all]`` marker:
- Brotli, zopfli (compress assets plugin)
- Boto (upload to S3 plugin)
diff --git a/readthedocs.yml b/readthedocs.yml
index febce40..b4d0b25 100644
--- a/readthedocs.yml
+++ b/readthedocs.yml
@@ -1,8 +1,12 @@
version: 2
+build:
+ image: latest
+
python:
version: 3.7
install:
- method: pip
path: .
- - requirements: requirements-doc.txt
+ extra_requirements:
+ - docs
diff --git a/requirements-doc.txt b/requirements-doc.txt
deleted file mode 100644
index 38f3862..0000000
--- a/requirements-doc.txt
+++ /dev/null
@@ -1,3 +0,0 @@
--r requirements.txt
-alabaster
-Sphinx
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index b037f6b..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-blinker
-boto
-brotli
-click
-coverage
-feedgenerator
-Jinja2
-Markdown
-Pillow
-pilkit
-pytest
-pytest-cov
-zopfli
diff --git a/setup.cfg b/setup.cfg
index 2e933db..67bab00 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -38,6 +38,8 @@ install_requires =
[options.extras_require]
all = boto; brotli; feedgenerator; zopfli
+tests = pytest; pytest-cov
+docs = Sphinx; alabaster
[options.packages.find]
exclude =
diff --git a/sigal/__init__.py b/sigal/__init__.py
index a66ea90..9bc5684 100644
--- a/sigal/__init__.py
+++ b/sigal/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2018 - Simon Conseil
+# Copyright (c) 2009-2020 - Simon Conseil
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
@@ -74,7 +74,7 @@ def init(path):
with open(path, 'w', encoding='utf-8') as f:
f.write(conf.decode('utf8'))
- print("Sample config file created: {}".format(path))
+ print(f"Sample config file created: {path}")
@main.command()
@@ -224,11 +224,11 @@ def serve(destination, port, config):
.format(destination=destination, config=config))
sys.exit(2)
- print('DESTINATION : {}'.format(destination))
+ print(f'DESTINATION : {destination}')
os.chdir(destination)
Handler = server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", port), Handler, False)
- print(" * Running on http://127.0.0.1:{}/".format(port))
+ print(f" * Running on http://127.0.0.1:{port}/")
try:
httpd.allow_reuse_address = True
@@ -255,7 +255,7 @@ def set_meta(target, keys, overwrite=False):
"""
if not os.path.exists(target):
- sys.stderr.write("The target {} does not exist.\n".format(target))
+ sys.stderr.write(f"The target {target} does not exist.\n")
sys.exit(1)
if len(keys) < 2 or len(keys) % 2 > 0:
sys.stderr.write("Need an even number of arguments.\n")
@@ -273,5 +273,5 @@ def set_meta(target, keys, overwrite=False):
with open(descfile, "w") as fp:
for i in range(len(keys) // 2):
k, v = keys[i * 2:(i + 1) * 2]
- fp.write("{}: {}\n".format(k.capitalize(), v))
+ fp.write(f"{k.capitalize()}: {v}\n")
print("{} metadata key(s) written to {}".format(len(keys) // 2, descfile))
diff --git a/sigal/gallery.py b/sigal/gallery.py
index 5625130..4952431 100644
--- a/sigal/gallery.py
+++ b/sigal/gallery.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2018 - Simon Conseil
+# Copyright (c) 2009-2020 - Simon Conseil
# Copyright (c) 2013 - Christophe-Marie Duquesne
# Copyright (c) 2014 - Jonas Kaufmann
# Copyright (c) 2015 - François D.
@@ -91,7 +91,7 @@ class Media:
signals.media_initialized.send(self)
def __repr__(self):
- return "<%s>(%r)" % (self.__class__.__name__, str(self))
+ return "<{}>({!r})".format(self.__class__.__name__, str(self))
def __str__(self):
return join(self.path, self.filename)
@@ -195,7 +195,7 @@ class Image(Media):
if self.raw_exif and self.ext in ('.jpg', '.jpeg') else None)
def _get_metadata(self):
- super(Image, self)._get_metadata()
+ super()._get_metadata()
# If a title or description hasn't been obtained by other means, look
# for the information in IPTC fields
if self.title and self.description:
@@ -244,7 +244,7 @@ class Video(Media):
type = 'video'
def __init__(self, filename, path, settings):
- super(Video, self).__init__(filename, path, settings)
+ super().__init__(filename, path, settings)
base, ext = splitext(filename)
self.src_filename = filename
self.date = self._get_file_date()
@@ -322,12 +322,12 @@ class Album:
signals.album_initialized.send(self)
def __repr__(self):
- return "<%s>(path=%r, title=%r)" % (self.__class__.__name__, self.path,
- self.title)
+ return "<{}>(path={!r}, title={!r})".format(
+ self.__class__.__name__, self.path, self.title)
def __str__(self):
- return ('{} : '.format(self.path) +
- ', '.join('{} {}s'.format(count, _type)
+ return (f'{self.path} : ' +
+ ', '.join(f'{count} {_type}s'
for _type, count in self.medias_count.items()))
def __len__(self):
@@ -544,7 +544,7 @@ class Album:
return None
-class Gallery(object):
+class Gallery:
def __init__(self, settings, ncpu=None, quiet=False):
self.settings = settings
diff --git a/sigal/image.py b/sigal/image.py
index 81ac2c6..f19132f 100644
--- a/sigal/image.py
+++ b/sigal/image.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2018 - Simon Conseil
+# Copyright (c) 2009-2020 - Simon Conseil
# Copyright (c) 2015 - François D.
# Copyright (c) 2018 - Edwin Steele
@@ -105,7 +105,7 @@ def generate_image(source, outname, settings, options=None):
if settings['autorotate_images']:
try:
img = Transpose().process(img)
- except (IOError, IndexError):
+ except (OSError, IndexError):
pass
# Resize the image
@@ -195,7 +195,7 @@ def get_size(file_path):
"""Return image size (width and height)."""
try:
im = _read_image(file_path)
- except (IOError, IndexError, TypeError, AttributeError) as e:
+ except (OSError, IndexError, TypeError, AttributeError) as e:
logger = logging.getLogger(__name__)
logger.error("Could not read size of %s due to %r", file_path, e)
else:
diff --git a/sigal/log.py b/sigal/log.py
index c5d2972..9dde29a 100644
--- a/sigal/log.py
+++ b/sigal/log.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2013-2018 - Simon Conseil
+# Copyright (c) 2013-2020 - Simon Conseil
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
diff --git a/sigal/plugins/compress_assets.py b/sigal/plugins/compress_assets.py
index 723c11b..86814a2 100644
--- a/sigal/plugins/compress_assets.py
+++ b/sigal/plugins/compress_assets.py
@@ -83,7 +83,7 @@ class BaseCompressor:
file_stats = None
compressed_stats = None
- compressed_filename = '{}.{}'.format(filename, self.suffix)
+ compressed_filename = f'{filename}.{self.suffix}'
try:
file_stats = os.stat(filename)
compressed_stats = os.stat(compressed_filename)
@@ -146,7 +146,7 @@ def get_compressor(settings):
logger.error('Unable to import brotli module')
else:
- logger.error('No such compressor {}'.format(name))
+ logger.error(f'No such compressor {name}')
def compress_gallery(gallery):
diff --git a/sigal/plugins/feeds.py b/sigal/plugins/feeds.py
index 0b09044..0d67343 100644
--- a/sigal/plugins/feeds.py
+++ b/sigal/plugins/feeds.py
@@ -58,9 +58,9 @@ def generate_feed(gallery, medias, feed_type=None, feed_url='', nb_items=0):
for item in medias[:nb_items]:
if theme == 'galleria':
- link = '%s/%s/#%s' % (base_url, item.path, item.url)
+ link = f'{base_url}/{item.path}/#{item.url}'
else:
- link = '%s/%s/' % (base_url, item.path)
+ link = f'{base_url}/{item.path}/'
feed.add_item(
title=Markup.escape(item.title or item.url),
@@ -68,7 +68,7 @@ def generate_feed(gallery, medias, feed_type=None, feed_url='', nb_items=0):
# unique_id='tag:%s,%s:%s' % (urlparse(link).netloc,
# item.date.date(),
# urlparse(link).path.lstrip('/')),
- description='' % (base_url, item.path,
+ description='
'.format(base_url, item.path,
item.thumbnail),
# categories=item.tags if hasattr(item, 'tags') else None,
author_name=getattr(item, 'author', ''),
diff --git a/sigal/plugins/media_page.py b/sigal/plugins/media_page.py
index 2cb52c3..4f5e508 100644
--- a/sigal/plugins/media_page.py
+++ b/sigal/plugins/media_page.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2018 - Simon Conseil
+# Copyright (c) 2009-2020 - Simon Conseil
# Copyright (c) 2014 - Jamie Starke
# Permission is hereby granted, free of charge, to any person obtaining a copy
diff --git a/sigal/plugins/nomedia.py b/sigal/plugins/nomedia.py
index 8881e77..ec9ed02 100644
--- a/sigal/plugins/nomedia.py
+++ b/sigal/plugins/nomedia.py
@@ -104,7 +104,7 @@ def filter_nomedia(album, settings=None):
album.medias = []
else:
- with open(nomediapath, "r") as nomediaFile:
+ with open(nomediapath) as nomediaFile:
logger.info("Found a .nomedia file in %s, ignoring its "
"entries", album.name)
ignored = nomediaFile.read().split("\n")
diff --git a/sigal/settings.py b/sigal/settings.py
index 8110d30..630bb08 100644
--- a/sigal/settings.py
+++ b/sigal/settings.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2018 - Simon Conseil
+# Copyright (c) 2009-2020 - Simon Conseil
# Copyright (c) 2013 - Christophe-Marie Duquesne
# Copyright (c) 2017 - Mate Lakat
@@ -86,7 +86,7 @@ _DEFAULT_CONFIG = {
}
-class Status(object):
+class Status:
SUCCESS = 0
FAILURE = 1
diff --git a/sigal/utils.py b/sigal/utils.py
index c828856..c4e10ed 100644
--- a/sigal/utils.py
+++ b/sigal/utils.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2011-2018 - Simon Conseil
+# Copyright (c) 2011-2020 - Simon Conseil
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
@@ -32,7 +32,7 @@ VIDEO_MIMES = {'.mp4': 'video/mp4',
MD = None
-class Devnull(object):
+class Devnull:
"""'Black hole' for output that should not be printed"""
def write(self, *_):
@@ -82,7 +82,7 @@ def read_markdown(filename):
# Use utf-8-sig codec to remove BOM if it is present. This is only possible
# this way prior to feeding the text to the markdown parser (which would
# also default to pure utf-8)
- with open(filename, 'r', encoding='utf-8-sig') as f:
+ with open(filename, encoding='utf-8-sig') as f:
text = f.read()
if MD is None:
@@ -122,7 +122,7 @@ def get_mime(ext):
return VIDEO_MIMES[ext]
-class cached_property(object):
+class cached_property:
""" A property that is only computed once per instance and then replaces
itself with an ordinary attribute. Deleting the attribute resets the
property.
diff --git a/sigal/video.py b/sigal/video.py
index c53980f..da67fcc 100644
--- a/sigal/video.py
+++ b/sigal/video.py
@@ -1,5 +1,5 @@
# Copyright (c) 2013 - Christophe-Marie Duquesne
-# Copyright (c) 2013-2018 - Simon Conseil
+# Copyright (c) 2013-2020 - Simon Conseil
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
diff --git a/sigal/writer.py b/sigal/writer.py
index 35e1021..38be978 100644
--- a/sigal/writer.py
+++ b/sigal/writer.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2018 - Simon Conseil
+# Copyright (c) 2009-2020 - Simon Conseil
# Copyright (c) 2013 - Christophe-Marie Duquesne
# Copyright (c) 2018 - Edwin Steele
@@ -37,7 +37,7 @@ THEMES_PATH = os.path.normpath(os.path.join(
os.path.abspath(os.path.dirname(__file__)), 'themes'))
-class AbstractWriter(object):
+class AbstractWriter:
template_file = None
def __init__(self, settings, index_title=''):
diff --git a/tests/conftest.py b/tests/conftest.py
index b740ee5..a6811a2 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -39,4 +39,4 @@ def disconnect_signals():
def pytest_report_header(config):
- return "project deps: Pillow-{}".format(PIL.__version__)
+ return f"project deps: Pillow-{PIL.__version__}"
diff --git a/tests/test_gallery.py b/tests/test_gallery.py
index 849fa10..1a48ca1 100644
--- a/tests/test_gallery.py
+++ b/tests/test_gallery.py
@@ -83,7 +83,7 @@ def test_media(settings):
assert m.title == "Foo Bar"
assert m.description == "
This is a funny description of this image
" - assert repr(m) == "