Browse Source

Merge branch 'misc'

pull/360/head
Simon Conseil 6 years ago
parent
commit
a2b98bbf4e
  1. 2
      .travis.yml
  2. 2
      LICENSE
  3. 24
      docs/changelog.py
  4. 2
      docs/conf.py
  5. 2
      docs/contribute.rst
  6. 6
      docs/installation.rst
  7. 6
      readthedocs.yml
  8. 3
      requirements-doc.txt
  9. 13
      requirements.txt
  10. 2
      setup.cfg
  11. 12
      sigal/__init__.py
  12. 18
      sigal/gallery.py
  13. 6
      sigal/image.py
  14. 2
      sigal/log.py
  15. 4
      sigal/plugins/compress_assets.py
  16. 6
      sigal/plugins/feeds.py
  17. 2
      sigal/plugins/media_page.py
  18. 2
      sigal/plugins/nomedia.py
  19. 4
      sigal/settings.py
  20. 8
      sigal/utils.py
  21. 2
      sigal/video.py
  22. 4
      sigal/writer.py
  23. 2
      tests/conftest.py
  24. 4
      tests/test_gallery.py
  25. 8
      tox.ini

2
.travis.yml

@ -34,7 +34,7 @@ matrix:
install: pip install -U tox-travis install: pip install -U tox-travis
script: tox script: tox -- -v
after_success: after_success:
- pip install codecov - pip install codecov

2
LICENSE

@ -1,5 +1,5 @@
The MIT License (MIT) 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to of this software and associated documentation files (the "Software"), to

24
docs/changelog.py

@ -51,7 +51,7 @@ A total of %d pull requests were merged for this release.
def get_authors(revision_range): def get_authors(revision_range):
pat = u'^.*\\t(.*)$' pat = '^.*\\t(.*)$'
lst_release, cur_release = [r.strip() for r in revision_range.split('..')] lst_release, cur_release = [r.strip() for r in revision_range.split('..')]
# authors, in current release and previous to current release. # authors, in current release and previous to current release.
@ -61,7 +61,7 @@ def get_authors(revision_range):
re.M)) re.M))
# Append '+' to new authors. # 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() authors.sort()
return authors return authors
@ -72,17 +72,17 @@ def get_pull_requests(repo, revision_range):
# From regular merges # From regular merges
merges = this_repo.git.log( merges = this_repo.git.log(
'--oneline', '--merges', revision_range) '--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) prnums.extend(int(s) for s in issues)
# From Homu merges (Auto merges) # 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) prnums.extend(int(s) for s in issues)
# From fast forward squash-merges # From fast forward squash-merges
commits = this_repo.git.log( commits = this_repo.git.log(
'--oneline', '--no-merges', '--first-parent', revision_range) '--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) prnums.extend(int(s) for s in issues)
# get PR data from github repo # get PR data from github repo
@ -99,19 +99,19 @@ def main(token, revision_range):
# document authors # document authors
authors = get_authors(revision_range) authors = get_authors(revision_range)
heading = u"Contributors" heading = "Contributors"
print() print()
print(heading) print(heading)
print("=" * len(heading)) print("=" * len(heading))
print(author_msg % len(authors)) print(author_msg % len(authors))
for s in authors: for s in authors:
print(u'* ' + s) print('* ' + s)
# document pull requests # document pull requests
pull_requests = get_pull_requests(github_repo, revision_range) pull_requests = get_pull_requests(github_repo, revision_range)
heading = u"Pull requests merged" heading = "Pull requests merged"
pull_msg = u"* `#{0} <{1}>`__: {2}" pull_msg = "* `#{0} <{1}>`__: {2}"
print() print()
print(heading) print(heading)
@ -119,11 +119,11 @@ def main(token, revision_range):
print(pull_request_msg % len(pull_requests)) print(pull_request_msg % len(pull_requests))
for pull in 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: if len(title) > 60:
remainder = re.sub(u"\\s.*$", u"...", title[60:]) remainder = re.sub("\\s.*$", "...", title[60:])
if len(remainder) > 20: if len(remainder) > 20:
remainder = title[:80] + u"..." remainder = title[:80] + "..."
else: else:
title = title[:60] + remainder title = title[:60] + remainder
print(pull_msg.format(pull.number, pull.html_url, title)) print(pull_msg.format(pull.number, pull.html_url, title))

2
docs/conf.py

@ -38,7 +38,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = 'Sigal' 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 # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the

2
docs/contribute.rst

@ -21,7 +21,7 @@ Install sigal in development mode::
Install additional dependencies for development (Sphinx, pytest), and optional Install additional dependencies for development (Sphinx, pytest), and optional
dependencies:: dependencies::
pip install -r requirements.txt pip install -e .\[all,tests\]
Building the docs Building the docs
----------------- -----------------

6
docs/installation.rst

@ -8,7 +8,7 @@ With pip::
Or to install with optional dependencies (listed below):: 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`. To install the development version, see the :doc:`contribute`.
@ -29,8 +29,8 @@ The mandatory dependencies are:
- Pillow - Pillow
- Python Markdown - Python Markdown
There are also a number of optional dependencies for the :doc:`plugins`, listed There are also a number of optional dependencies for the :doc:`plugins`,
in the ``requirements.txt`` file: installable with the ``[all]`` marker:
- Brotli, zopfli (compress assets plugin) - Brotli, zopfli (compress assets plugin)
- Boto (upload to S3 plugin) - Boto (upload to S3 plugin)

6
readthedocs.yml

@ -1,8 +1,12 @@
version: 2 version: 2
build:
image: latest
python: python:
version: 3.7 version: 3.7
install: install:
- method: pip - method: pip
path: . path: .
- requirements: requirements-doc.txt extra_requirements:
- docs

3
requirements-doc.txt

@ -1,3 +0,0 @@
-r requirements.txt
alabaster
Sphinx

13
requirements.txt

@ -1,13 +0,0 @@
blinker
boto
brotli
click
coverage
feedgenerator
Jinja2
Markdown
Pillow
pilkit
pytest
pytest-cov
zopfli

2
setup.cfg

@ -38,6 +38,8 @@ install_requires =
[options.extras_require] [options.extras_require]
all = boto; brotli; feedgenerator; zopfli all = boto; brotli; feedgenerator; zopfli
tests = pytest; pytest-cov
docs = Sphinx; alabaster
[options.packages.find] [options.packages.find]
exclude = exclude =

12
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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to # 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: with open(path, 'w', encoding='utf-8') as f:
f.write(conf.decode('utf8')) f.write(conf.decode('utf8'))
print("Sample config file created: {}".format(path)) print(f"Sample config file created: {path}")
@main.command() @main.command()
@ -224,11 +224,11 @@ def serve(destination, port, config):
.format(destination=destination, config=config)) .format(destination=destination, config=config))
sys.exit(2) sys.exit(2)
print('DESTINATION : {}'.format(destination)) print(f'DESTINATION : {destination}')
os.chdir(destination) os.chdir(destination)
Handler = server.SimpleHTTPRequestHandler Handler = server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", port), Handler, False) 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: try:
httpd.allow_reuse_address = True httpd.allow_reuse_address = True
@ -255,7 +255,7 @@ def set_meta(target, keys, overwrite=False):
""" """
if not os.path.exists(target): 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) sys.exit(1)
if len(keys) < 2 or len(keys) % 2 > 0: if len(keys) < 2 or len(keys) % 2 > 0:
sys.stderr.write("Need an even number of arguments.\n") 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: with open(descfile, "w") as fp:
for i in range(len(keys) // 2): for i in range(len(keys) // 2):
k, v = keys[i * 2:(i + 1) * 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)) print("{} metadata key(s) written to {}".format(len(keys) // 2, descfile))

18
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) 2013 - Christophe-Marie Duquesne
# Copyright (c) 2014 - Jonas Kaufmann # Copyright (c) 2014 - Jonas Kaufmann
# Copyright (c) 2015 - François D. # Copyright (c) 2015 - François D.
@ -91,7 +91,7 @@ class Media:
signals.media_initialized.send(self) signals.media_initialized.send(self)
def __repr__(self): def __repr__(self):
return "<%s>(%r)" % (self.__class__.__name__, str(self)) return "<{}>({!r})".format(self.__class__.__name__, str(self))
def __str__(self): def __str__(self):
return join(self.path, self.filename) 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) if self.raw_exif and self.ext in ('.jpg', '.jpeg') else None)
def _get_metadata(self): 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 # If a title or description hasn't been obtained by other means, look
# for the information in IPTC fields # for the information in IPTC fields
if self.title and self.description: if self.title and self.description:
@ -244,7 +244,7 @@ class Video(Media):
type = 'video' type = 'video'
def __init__(self, filename, path, settings): def __init__(self, filename, path, settings):
super(Video, self).__init__(filename, path, settings) super().__init__(filename, path, settings)
base, ext = splitext(filename) base, ext = splitext(filename)
self.src_filename = filename self.src_filename = filename
self.date = self._get_file_date() self.date = self._get_file_date()
@ -322,12 +322,12 @@ class Album:
signals.album_initialized.send(self) signals.album_initialized.send(self)
def __repr__(self): def __repr__(self):
return "<%s>(path=%r, title=%r)" % (self.__class__.__name__, self.path, return "<{}>(path={!r}, title={!r})".format(
self.title) self.__class__.__name__, self.path, self.title)
def __str__(self): def __str__(self):
return ('{} : '.format(self.path) + return (f'{self.path} : ' +
', '.join('{} {}s'.format(count, _type) ', '.join(f'{count} {_type}s'
for _type, count in self.medias_count.items())) for _type, count in self.medias_count.items()))
def __len__(self): def __len__(self):
@ -544,7 +544,7 @@ class Album:
return None return None
class Gallery(object): class Gallery:
def __init__(self, settings, ncpu=None, quiet=False): def __init__(self, settings, ncpu=None, quiet=False):
self.settings = settings self.settings = settings

6
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) 2015 - François D.
# Copyright (c) 2018 - Edwin Steele # Copyright (c) 2018 - Edwin Steele
@ -105,7 +105,7 @@ def generate_image(source, outname, settings, options=None):
if settings['autorotate_images']: if settings['autorotate_images']:
try: try:
img = Transpose().process(img) img = Transpose().process(img)
except (IOError, IndexError): except (OSError, IndexError):
pass pass
# Resize the image # Resize the image
@ -195,7 +195,7 @@ def get_size(file_path):
"""Return image size (width and height).""" """Return image size (width and height)."""
try: try:
im = _read_image(file_path) im = _read_image(file_path)
except (IOError, IndexError, TypeError, AttributeError) as e: except (OSError, IndexError, TypeError, AttributeError) as e:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.error("Could not read size of %s due to %r", file_path, e) logger.error("Could not read size of %s due to %r", file_path, e)
else: else:

2
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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to # of this software and associated documentation files (the "Software"), to

4
sigal/plugins/compress_assets.py

@ -83,7 +83,7 @@ class BaseCompressor:
file_stats = None file_stats = None
compressed_stats = None compressed_stats = None
compressed_filename = '{}.{}'.format(filename, self.suffix) compressed_filename = f'{filename}.{self.suffix}'
try: try:
file_stats = os.stat(filename) file_stats = os.stat(filename)
compressed_stats = os.stat(compressed_filename) compressed_stats = os.stat(compressed_filename)
@ -146,7 +146,7 @@ def get_compressor(settings):
logger.error('Unable to import brotli module') logger.error('Unable to import brotli module')
else: else:
logger.error('No such compressor {}'.format(name)) logger.error(f'No such compressor {name}')
def compress_gallery(gallery): def compress_gallery(gallery):

6
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]: for item in medias[:nb_items]:
if theme == 'galleria': if theme == 'galleria':
link = '%s/%s/#%s' % (base_url, item.path, item.url) link = f'{base_url}/{item.path}/#{item.url}'
else: else:
link = '%s/%s/' % (base_url, item.path) link = f'{base_url}/{item.path}/'
feed.add_item( feed.add_item(
title=Markup.escape(item.title or item.url), 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, # unique_id='tag:%s,%s:%s' % (urlparse(link).netloc,
# item.date.date(), # item.date.date(),
# urlparse(link).path.lstrip('/')), # urlparse(link).path.lstrip('/')),
description='<img src="%s/%s/%s" />' % (base_url, item.path, description='<img src="{}/{}/{}" />'.format(base_url, item.path,
item.thumbnail), item.thumbnail),
# categories=item.tags if hasattr(item, 'tags') else None, # categories=item.tags if hasattr(item, 'tags') else None,
author_name=getattr(item, 'author', ''), author_name=getattr(item, 'author', ''),

2
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 # Copyright (c) 2014 - Jamie Starke
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy

2
sigal/plugins/nomedia.py

@ -104,7 +104,7 @@ def filter_nomedia(album, settings=None):
album.medias = [] album.medias = []
else: else:
with open(nomediapath, "r") as nomediaFile: with open(nomediapath) as nomediaFile:
logger.info("Found a .nomedia file in %s, ignoring its " logger.info("Found a .nomedia file in %s, ignoring its "
"entries", album.name) "entries", album.name)
ignored = nomediaFile.read().split("\n") ignored = nomediaFile.read().split("\n")

4
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) 2013 - Christophe-Marie Duquesne
# Copyright (c) 2017 - Mate Lakat # Copyright (c) 2017 - Mate Lakat
@ -86,7 +86,7 @@ _DEFAULT_CONFIG = {
} }
class Status(object): class Status:
SUCCESS = 0 SUCCESS = 0
FAILURE = 1 FAILURE = 1

8
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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to # of this software and associated documentation files (the "Software"), to
@ -32,7 +32,7 @@ VIDEO_MIMES = {'.mp4': 'video/mp4',
MD = None MD = None
class Devnull(object): class Devnull:
"""'Black hole' for output that should not be printed""" """'Black hole' for output that should not be printed"""
def write(self, *_): 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 # 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 # this way prior to feeding the text to the markdown parser (which would
# also default to pure utf-8) # 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() text = f.read()
if MD is None: if MD is None:
@ -122,7 +122,7 @@ def get_mime(ext):
return VIDEO_MIMES[ext] return VIDEO_MIMES[ext]
class cached_property(object): class cached_property:
""" A property that is only computed once per instance and then replaces """ A property that is only computed once per instance and then replaces
itself with an ordinary attribute. Deleting the attribute resets the itself with an ordinary attribute. Deleting the attribute resets the
property. property.

2
sigal/video.py

@ -1,5 +1,5 @@
# Copyright (c) 2013 - Christophe-Marie Duquesne # 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to # of this software and associated documentation files (the "Software"), to

4
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) 2013 - Christophe-Marie Duquesne
# Copyright (c) 2018 - Edwin Steele # 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')) os.path.abspath(os.path.dirname(__file__)), 'themes'))
class AbstractWriter(object): class AbstractWriter:
template_file = None template_file = None
def __init__(self, settings, index_title=''): def __init__(self, settings, index_title=''):

2
tests/conftest.py

@ -39,4 +39,4 @@ def disconnect_signals():
def pytest_report_header(config): def pytest_report_header(config):
return "project deps: Pillow-{}".format(PIL.__version__) return f"project deps: Pillow-{PIL.__version__}"

4
tests/test_gallery.py

@ -83,7 +83,7 @@ def test_media(settings):
assert m.title == "Foo Bar" assert m.title == "Foo Bar"
assert m.description == "<p>This is a funny description of this image</p>" 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 assert str(m) == file_path
@ -244,7 +244,7 @@ def test_gallery(settings, tmpdir):
out_html = os.path.join(settings['destination'], 'index.html') out_html = os.path.join(settings['destination'], 'index.html')
assert os.path.isfile(out_html) assert os.path.isfile(out_html)
with open(out_html, 'r') as f: with open(out_html) as f:
html = f.read() html = f.read()
assert '<title>Sigal test gallery</title>' in html assert '<title>Sigal test gallery</title>' in html

8
tox.ini

@ -9,8 +9,9 @@ python =
3.8: py38 3.8: py38
[testenv] [testenv]
deps = extras =
-r {toxinidir}/requirements.txt all
tests
commands = pytest --cov sigal --cov-report term-missing tests/ commands = pytest --cov sigal --cov-report term-missing tests/
[testenv:check] [testenv:check]
@ -28,8 +29,7 @@ commands =
[testenv:doc] [testenv:doc]
whitelist_externals = make whitelist_externals = make
deps = extras = docs
-r {toxinidir}/requirements-doc.txt
usedevelop = true usedevelop = true
commands = commands =
make -C docs html make -C docs html

Loading…
Cancel
Save