Merge branch 'misc'
This commit is contained in:
@@ -34,7 +34,7 @@ matrix:
|
||||
|
||||
install: pip install -U tox-travis
|
||||
|
||||
script: tox
|
||||
script: tox -- -v
|
||||
|
||||
after_success:
|
||||
- pip install codecov
|
||||
|
||||
2
LICENSE
2
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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
-----------------
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
version: 2
|
||||
|
||||
build:
|
||||
image: latest
|
||||
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
- requirements: requirements-doc.txt
|
||||
extra_requirements:
|
||||
- docs
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
-r requirements.txt
|
||||
alabaster
|
||||
Sphinx
|
||||
@@ -1,13 +0,0 @@
|
||||
blinker
|
||||
boto
|
||||
brotli
|
||||
click
|
||||
coverage
|
||||
feedgenerator
|
||||
Jinja2
|
||||
Markdown
|
||||
Pillow
|
||||
pilkit
|
||||
pytest
|
||||
pytest-cov
|
||||
zopfli
|
||||
@@ -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 =
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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='<img src="%s/%s/%s" />' % (base_url, item.path,
|
||||
description='<img src="{}/{}/{}" />'.format(base_url, item.path,
|
||||
item.thumbnail),
|
||||
# categories=item.tags if hasattr(item, 'tags') else None,
|
||||
author_name=getattr(item, 'author', ''),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=''):
|
||||
|
||||
@@ -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__}"
|
||||
|
||||
@@ -83,7 +83,7 @@ def test_media(settings):
|
||||
assert m.title == "Foo Bar"
|
||||
assert m.description == "<p>This is a funny description of this image</p>"
|
||||
|
||||
assert repr(m) == "<Media>('{}')".format(file_path)
|
||||
assert repr(m) == f"<Media>('{file_path}')"
|
||||
assert str(m) == file_path
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ def test_gallery(settings, tmpdir):
|
||||
out_html = os.path.join(settings['destination'], 'index.html')
|
||||
assert os.path.isfile(out_html)
|
||||
|
||||
with open(out_html, 'r') as f:
|
||||
with open(out_html) as f:
|
||||
html = f.read()
|
||||
|
||||
assert '<title>Sigal test gallery</title>' in html
|
||||
|
||||
8
tox.ini
8
tox.ini
@@ -9,8 +9,9 @@ python =
|
||||
3.8: py38
|
||||
|
||||
[testenv]
|
||||
deps =
|
||||
-r {toxinidir}/requirements.txt
|
||||
extras =
|
||||
all
|
||||
tests
|
||||
commands = pytest --cov sigal --cov-report term-missing tests/
|
||||
|
||||
[testenv:check]
|
||||
@@ -28,8 +29,7 @@ commands =
|
||||
|
||||
[testenv:doc]
|
||||
whitelist_externals = make
|
||||
deps =
|
||||
-r {toxinidir}/requirements-doc.txt
|
||||
extras = docs
|
||||
usedevelop = true
|
||||
commands =
|
||||
make -C docs html
|
||||
|
||||
Reference in New Issue
Block a user