diff --git a/pelican/__init__.py b/pelican/__init__.py
index fcdda8a4..a0ff4989 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -9,19 +9,25 @@ import sys
import time
import traceback
from collections.abc import Iterable
+
# Combines all paths to `pelican` package accessible from `sys.path`
# Makes it possible to install `pelican` and namespace plugins into different
# locations in the file system (e.g. pip with `-e` or `--user`)
from pkgutil import extend_path
+
__path__ = extend_path(__path__, __name__)
# pelican.log has to be the first pelican module to be loaded
# because logging.setLoggerClass has to be called before logging.getLogger
from pelican.log import console
from pelican.log import init as init_logging
-from pelican.generators import (ArticlesGenerator, # noqa: I100
- PagesGenerator, SourceFileGenerator,
- StaticGenerator, TemplatePagesGenerator)
+from pelican.generators import (
+ ArticlesGenerator, # noqa: I100
+ PagesGenerator,
+ SourceFileGenerator,
+ StaticGenerator,
+ TemplatePagesGenerator,
+)
from pelican.plugins import signals
from pelican.plugins._utils import get_plugin_name, load_plugins
from pelican.readers import Readers
@@ -35,12 +41,11 @@ try:
except Exception:
__version__ = "unknown"
-DEFAULT_CONFIG_NAME = 'pelicanconf.py'
+DEFAULT_CONFIG_NAME = "pelicanconf.py"
logger = logging.getLogger(__name__)
class Pelican:
-
def __init__(self, settings):
"""Pelican initialization
@@ -50,35 +55,34 @@ class Pelican:
# define the default settings
self.settings = settings
- self.path = settings['PATH']
- self.theme = settings['THEME']
- self.output_path = settings['OUTPUT_PATH']
- self.ignore_files = settings['IGNORE_FILES']
- self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY']
- self.output_retention = settings['OUTPUT_RETENTION']
+ self.path = settings["PATH"]
+ self.theme = settings["THEME"]
+ self.output_path = settings["OUTPUT_PATH"]
+ self.ignore_files = settings["IGNORE_FILES"]
+ self.delete_outputdir = settings["DELETE_OUTPUT_DIRECTORY"]
+ self.output_retention = settings["OUTPUT_RETENTION"]
self.init_path()
self.init_plugins()
signals.initialized.send(self)
def init_path(self):
- if not any(p in sys.path for p in ['', os.curdir]):
+ if not any(p in sys.path for p in ["", os.curdir]):
logger.debug("Adding current directory to system path")
- sys.path.insert(0, '')
+ sys.path.insert(0, "")
def init_plugins(self):
self.plugins = []
for plugin in load_plugins(self.settings):
name = get_plugin_name(plugin)
- logger.debug('Registering plugin `%s`', name)
+ logger.debug("Registering plugin `%s`", name)
try:
plugin.register()
self.plugins.append(plugin)
except Exception as e:
- logger.error('Cannot register plugin `%s`\n%s',
- name, e)
+ logger.error("Cannot register plugin `%s`\n%s", name, e)
- self.settings['PLUGINS'] = [get_plugin_name(p) for p in self.plugins]
+ self.settings["PLUGINS"] = [get_plugin_name(p) for p in self.plugins]
def run(self):
"""Run the generators and return"""
@@ -87,10 +91,10 @@ class Pelican:
context = self.settings.copy()
# Share these among all the generators and content objects
# They map source paths to Content objects or None
- context['generated_content'] = {}
- context['static_links'] = set()
- context['static_content'] = {}
- context['localsiteurl'] = self.settings['SITEURL']
+ context["generated_content"] = {}
+ context["static_links"] = set()
+ context["static_content"] = {}
+ context["localsiteurl"] = self.settings["SITEURL"]
generators = [
cls(
@@ -99,23 +103,25 @@ class Pelican:
path=self.path,
theme=self.theme,
output_path=self.output_path,
- ) for cls in self._get_generator_classes()
+ )
+ for cls in self._get_generator_classes()
]
# Delete the output directory if (1) the appropriate setting is True
# and (2) that directory is not the parent of the source directory
- if (self.delete_outputdir
- and os.path.commonpath([os.path.realpath(self.output_path)]) !=
- os.path.commonpath([os.path.realpath(self.output_path),
- os.path.realpath(self.path)])):
+ if self.delete_outputdir and os.path.commonpath(
+ [os.path.realpath(self.output_path)]
+ ) != os.path.commonpath(
+ [os.path.realpath(self.output_path), os.path.realpath(self.path)]
+ ):
clean_output_dir(self.output_path, self.output_retention)
for p in generators:
- if hasattr(p, 'generate_context'):
+ if hasattr(p, "generate_context"):
p.generate_context()
for p in generators:
- if hasattr(p, 'refresh_metadata_intersite_links'):
+ if hasattr(p, "refresh_metadata_intersite_links"):
p.refresh_metadata_intersite_links()
signals.all_generators_finalized.send(generators)
@@ -123,61 +129,75 @@ class Pelican:
writer = self._get_writer()
for p in generators:
- if hasattr(p, 'generate_output'):
+ if hasattr(p, "generate_output"):
p.generate_output(writer)
signals.finalized.send(self)
- articles_generator = next(g for g in generators
- if isinstance(g, ArticlesGenerator))
- pages_generator = next(g for g in generators
- if isinstance(g, PagesGenerator))
+ articles_generator = next(
+ g for g in generators if isinstance(g, ArticlesGenerator)
+ )
+ pages_generator = next(g for g in generators if isinstance(g, PagesGenerator))
pluralized_articles = maybe_pluralize(
- (len(articles_generator.articles) +
- len(articles_generator.translations)),
- 'article',
- 'articles')
+ (len(articles_generator.articles) + len(articles_generator.translations)),
+ "article",
+ "articles",
+ )
pluralized_drafts = maybe_pluralize(
- (len(articles_generator.drafts) +
- len(articles_generator.drafts_translations)),
- 'draft',
- 'drafts')
+ (
+ len(articles_generator.drafts)
+ + len(articles_generator.drafts_translations)
+ ),
+ "draft",
+ "drafts",
+ )
pluralized_hidden_articles = maybe_pluralize(
- (len(articles_generator.hidden_articles) +
- len(articles_generator.hidden_translations)),
- 'hidden article',
- 'hidden articles')
+ (
+ len(articles_generator.hidden_articles)
+ + len(articles_generator.hidden_translations)
+ ),
+ "hidden article",
+ "hidden articles",
+ )
pluralized_pages = maybe_pluralize(
- (len(pages_generator.pages) +
- len(pages_generator.translations)),
- 'page',
- 'pages')
+ (len(pages_generator.pages) + len(pages_generator.translations)),
+ "page",
+ "pages",
+ )
pluralized_hidden_pages = maybe_pluralize(
- (len(pages_generator.hidden_pages) +
- len(pages_generator.hidden_translations)),
- 'hidden page',
- 'hidden pages')
+ (
+ len(pages_generator.hidden_pages)
+ + len(pages_generator.hidden_translations)
+ ),
+ "hidden page",
+ "hidden pages",
+ )
pluralized_draft_pages = maybe_pluralize(
- (len(pages_generator.draft_pages) +
- len(pages_generator.draft_translations)),
- 'draft page',
- 'draft pages')
+ (
+ len(pages_generator.draft_pages)
+ + len(pages_generator.draft_translations)
+ ),
+ "draft page",
+ "draft pages",
+ )
- console.print('Done: Processed {}, {}, {}, {}, {} and {} in {:.2f} seconds.'
- .format(
- pluralized_articles,
- pluralized_drafts,
- pluralized_hidden_articles,
- pluralized_pages,
- pluralized_hidden_pages,
- pluralized_draft_pages,
- time.time() - start_time))
+ console.print(
+ "Done: Processed {}, {}, {}, {}, {} and {} in {:.2f} seconds.".format(
+ pluralized_articles,
+ pluralized_drafts,
+ pluralized_hidden_articles,
+ pluralized_pages,
+ pluralized_hidden_pages,
+ pluralized_draft_pages,
+ time.time() - start_time,
+ )
+ )
def _get_generator_classes(self):
discovered_generators = [
(ArticlesGenerator, "internal"),
- (PagesGenerator, "internal")
+ (PagesGenerator, "internal"),
]
if self.settings["TEMPLATE_PAGES"]:
@@ -236,7 +256,7 @@ class PrintSettings(argparse.Action):
except Exception as e:
logger.critical("%s: %s", e.__class__.__name__, e)
console.print_exception()
- sys.exit(getattr(e, 'exitcode', 1))
+ sys.exit(getattr(e, "exitcode", 1))
if values:
# One or more arguments provided, so only print those settings
@@ -244,14 +264,16 @@ class PrintSettings(argparse.Action):
if setting in settings:
# Only add newline between setting name and value if dict
if isinstance(settings[setting], (dict, tuple, list)):
- setting_format = '\n{}:\n{}'
+ setting_format = "\n{}:\n{}"
else:
- setting_format = '\n{}: {}'
- console.print(setting_format.format(
- setting,
- pprint.pformat(settings[setting])))
+ setting_format = "\n{}: {}"
+ console.print(
+ setting_format.format(
+ setting, pprint.pformat(settings[setting])
+ )
+ )
else:
- console.print('\n{} is not a recognized setting.'.format(setting))
+ console.print("\n{} is not a recognized setting.".format(setting))
break
else:
# No argument was given to --print-settings, so print all settings
@@ -268,170 +290,258 @@ class ParseOverrides(argparse.Action):
k, v = item.split("=", 1)
except ValueError:
raise ValueError(
- 'Extra settings must be specified as KEY=VALUE pairs '
- f'but you specified {item}'
+ "Extra settings must be specified as KEY=VALUE pairs "
+ f"but you specified {item}"
)
try:
overrides[k] = json.loads(v)
except json.decoder.JSONDecodeError:
raise ValueError(
- f'Invalid JSON value: {v}. '
- 'Values specified via -e / --extra-settings flags '
- 'must be in JSON notation. '
- 'Use -e KEY=\'"string"\' to specify a string value; '
- '-e KEY=null to specify None; '
- '-e KEY=false (or true) to specify False (or True).'
+ f"Invalid JSON value: {v}. "
+ "Values specified via -e / --extra-settings flags "
+ "must be in JSON notation. "
+ "Use -e KEY='\"string\"' to specify a string value; "
+ "-e KEY=null to specify None; "
+ "-e KEY=false (or true) to specify False (or True)."
)
setattr(namespace, self.dest, overrides)
def parse_arguments(argv=None):
parser = argparse.ArgumentParser(
- description='A tool to generate a static blog, '
- ' with restructured text input files.',
- formatter_class=argparse.ArgumentDefaultsHelpFormatter
+ description="A tool to generate a static blog, "
+ " with restructured text input files.",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
- parser.add_argument(dest='path', nargs='?',
- help='Path where to find the content files.',
- default=None)
+ parser.add_argument(
+ dest="path",
+ nargs="?",
+ help="Path where to find the content files.",
+ default=None,
+ )
- parser.add_argument('-t', '--theme-path', dest='theme',
- help='Path where to find the theme templates. If not '
- 'specified, it will use the default one included with '
- 'pelican.')
+ parser.add_argument(
+ "-t",
+ "--theme-path",
+ dest="theme",
+ help="Path where to find the theme templates. If not "
+ "specified, it will use the default one included with "
+ "pelican.",
+ )
- parser.add_argument('-o', '--output', dest='output',
- help='Where to output the generated files. If not '
- 'specified, a directory will be created, named '
- '"output" in the current path.')
+ parser.add_argument(
+ "-o",
+ "--output",
+ dest="output",
+ help="Where to output the generated files. If not "
+ "specified, a directory will be created, named "
+ '"output" in the current path.',
+ )
- parser.add_argument('-s', '--settings', dest='settings',
- help='The settings of the application, this is '
- 'automatically set to {} if a file exists with this '
- 'name.'.format(DEFAULT_CONFIG_NAME))
+ parser.add_argument(
+ "-s",
+ "--settings",
+ dest="settings",
+ help="The settings of the application, this is "
+ "automatically set to {} if a file exists with this "
+ "name.".format(DEFAULT_CONFIG_NAME),
+ )
- parser.add_argument('-d', '--delete-output-directory',
- dest='delete_outputdir', action='store_true',
- default=None, help='Delete the output directory.')
+ parser.add_argument(
+ "-d",
+ "--delete-output-directory",
+ dest="delete_outputdir",
+ action="store_true",
+ default=None,
+ help="Delete the output directory.",
+ )
- parser.add_argument('-v', '--verbose', action='store_const',
- const=logging.INFO, dest='verbosity',
- help='Show all messages.')
+ parser.add_argument(
+ "-v",
+ "--verbose",
+ action="store_const",
+ const=logging.INFO,
+ dest="verbosity",
+ help="Show all messages.",
+ )
- parser.add_argument('-q', '--quiet', action='store_const',
- const=logging.CRITICAL, dest='verbosity',
- help='Show only critical errors.')
+ parser.add_argument(
+ "-q",
+ "--quiet",
+ action="store_const",
+ const=logging.CRITICAL,
+ dest="verbosity",
+ help="Show only critical errors.",
+ )
- parser.add_argument('-D', '--debug', action='store_const',
- const=logging.DEBUG, dest='verbosity',
- help='Show all messages, including debug messages.')
+ parser.add_argument(
+ "-D",
+ "--debug",
+ action="store_const",
+ const=logging.DEBUG,
+ dest="verbosity",
+ help="Show all messages, including debug messages.",
+ )
- parser.add_argument('--version', action='version', version=__version__,
- help='Print the pelican version and exit.')
+ parser.add_argument(
+ "--version",
+ action="version",
+ version=__version__,
+ help="Print the pelican version and exit.",
+ )
- parser.add_argument('-r', '--autoreload', dest='autoreload',
- action='store_true',
- help='Relaunch pelican each time a modification occurs'
- ' on the content files.')
+ parser.add_argument(
+ "-r",
+ "--autoreload",
+ dest="autoreload",
+ action="store_true",
+ help="Relaunch pelican each time a modification occurs"
+ " on the content files.",
+ )
- parser.add_argument('--print-settings', dest='print_settings', nargs='*',
- action=PrintSettings, metavar='SETTING_NAME',
- help='Print current configuration settings and exit. '
- 'Append one or more setting name arguments to see the '
- 'values for specific settings only.')
+ parser.add_argument(
+ "--print-settings",
+ dest="print_settings",
+ nargs="*",
+ action=PrintSettings,
+ metavar="SETTING_NAME",
+ help="Print current configuration settings and exit. "
+ "Append one or more setting name arguments to see the "
+ "values for specific settings only.",
+ )
- parser.add_argument('--relative-urls', dest='relative_paths',
- action='store_true',
- help='Use relative urls in output, '
- 'useful for site development')
+ parser.add_argument(
+ "--relative-urls",
+ dest="relative_paths",
+ action="store_true",
+ help="Use relative urls in output, " "useful for site development",
+ )
- parser.add_argument('--cache-path', dest='cache_path',
- help=('Directory in which to store cache files. '
- 'If not specified, defaults to "cache".'))
+ parser.add_argument(
+ "--cache-path",
+ dest="cache_path",
+ help=(
+ "Directory in which to store cache files. "
+ 'If not specified, defaults to "cache".'
+ ),
+ )
- parser.add_argument('--ignore-cache', action='store_true',
- dest='ignore_cache', help='Ignore content cache '
- 'from previous runs by not loading cache files.')
+ parser.add_argument(
+ "--ignore-cache",
+ action="store_true",
+ dest="ignore_cache",
+ help="Ignore content cache " "from previous runs by not loading cache files.",
+ )
- parser.add_argument('-w', '--write-selected', type=str,
- dest='selected_paths', default=None,
- help='Comma separated list of selected paths to write')
+ parser.add_argument(
+ "-w",
+ "--write-selected",
+ type=str,
+ dest="selected_paths",
+ default=None,
+ help="Comma separated list of selected paths to write",
+ )
- parser.add_argument('--fatal', metavar='errors|warnings',
- choices=('errors', 'warnings'), default='',
- help=('Exit the program with non-zero status if any '
- 'errors/warnings encountered.'))
+ parser.add_argument(
+ "--fatal",
+ metavar="errors|warnings",
+ choices=("errors", "warnings"),
+ default="",
+ help=(
+ "Exit the program with non-zero status if any "
+ "errors/warnings encountered."
+ ),
+ )
- parser.add_argument('--logs-dedup-min-level', default='WARNING',
- choices=('DEBUG', 'INFO', 'WARNING', 'ERROR'),
- help=('Only enable log de-duplication for levels equal'
- ' to or above the specified value'))
+ parser.add_argument(
+ "--logs-dedup-min-level",
+ default="WARNING",
+ choices=("DEBUG", "INFO", "WARNING", "ERROR"),
+ help=(
+ "Only enable log de-duplication for levels equal"
+ " to or above the specified value"
+ ),
+ )
- parser.add_argument('-l', '--listen', dest='listen', action='store_true',
- help='Serve content files via HTTP and port 8000.')
+ parser.add_argument(
+ "-l",
+ "--listen",
+ dest="listen",
+ action="store_true",
+ help="Serve content files via HTTP and port 8000.",
+ )
- parser.add_argument('-p', '--port', dest='port', type=int,
- help='Port to serve HTTP files at. (default: 8000)')
+ parser.add_argument(
+ "-p",
+ "--port",
+ dest="port",
+ type=int,
+ help="Port to serve HTTP files at. (default: 8000)",
+ )
- parser.add_argument('-b', '--bind', dest='bind',
- help='IP to bind to when serving files via HTTP '
- '(default: 127.0.0.1)')
+ parser.add_argument(
+ "-b",
+ "--bind",
+ dest="bind",
+ help="IP to bind to when serving files via HTTP " "(default: 127.0.0.1)",
+ )
- parser.add_argument('-e', '--extra-settings', dest='overrides',
- help='Specify one or more SETTING=VALUE pairs to '
- 'override settings. VALUE must be in JSON notation: '
- 'specify string values as SETTING=\'"some string"\'; '
- 'booleans as SETTING=true or SETTING=false; '
- 'None as SETTING=null.',
- nargs='*',
- action=ParseOverrides,
- default={})
+ parser.add_argument(
+ "-e",
+ "--extra-settings",
+ dest="overrides",
+ help="Specify one or more SETTING=VALUE pairs to "
+ "override settings. VALUE must be in JSON notation: "
+ "specify string values as SETTING='\"some string\"'; "
+ "booleans as SETTING=true or SETTING=false; "
+ "None as SETTING=null.",
+ nargs="*",
+ action=ParseOverrides,
+ default={},
+ )
args = parser.parse_args(argv)
if args.port is not None and not args.listen:
- logger.warning('--port without --listen has no effect')
+ logger.warning("--port without --listen has no effect")
if args.bind is not None and not args.listen:
- logger.warning('--bind without --listen has no effect')
+ logger.warning("--bind without --listen has no effect")
return args
def get_config(args):
- """Builds a config dictionary based on supplied `args`.
- """
+ """Builds a config dictionary based on supplied `args`."""
config = {}
if args.path:
- config['PATH'] = os.path.abspath(os.path.expanduser(args.path))
+ config["PATH"] = os.path.abspath(os.path.expanduser(args.path))
if args.output:
- config['OUTPUT_PATH'] = \
- os.path.abspath(os.path.expanduser(args.output))
+ config["OUTPUT_PATH"] = os.path.abspath(os.path.expanduser(args.output))
if args.theme:
abstheme = os.path.abspath(os.path.expanduser(args.theme))
- config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme
+ config["THEME"] = abstheme if os.path.exists(abstheme) else args.theme
if args.delete_outputdir is not None:
- config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir
+ config["DELETE_OUTPUT_DIRECTORY"] = args.delete_outputdir
if args.ignore_cache:
- config['LOAD_CONTENT_CACHE'] = False
+ config["LOAD_CONTENT_CACHE"] = False
if args.cache_path:
- config['CACHE_PATH'] = args.cache_path
+ config["CACHE_PATH"] = args.cache_path
if args.selected_paths:
- config['WRITE_SELECTED'] = args.selected_paths.split(',')
+ config["WRITE_SELECTED"] = args.selected_paths.split(",")
if args.relative_paths:
- config['RELATIVE_URLS'] = args.relative_paths
+ config["RELATIVE_URLS"] = args.relative_paths
if args.port is not None:
- config['PORT'] = args.port
+ config["PORT"] = args.port
if args.bind is not None:
- config['BIND'] = args.bind
- config['DEBUG'] = args.verbosity == logging.DEBUG
+ config["BIND"] = args.bind
+ config["DEBUG"] = args.verbosity == logging.DEBUG
config.update(args.overrides)
return config
def get_instance(args):
-
config_file = args.settings
if config_file is None and os.path.isfile(DEFAULT_CONFIG_NAME):
config_file = DEFAULT_CONFIG_NAME
@@ -439,9 +549,9 @@ def get_instance(args):
settings = read_settings(config_file, override=get_config(args))
- cls = settings['PELICAN_CLASS']
+ cls = settings["PELICAN_CLASS"]
if isinstance(cls, str):
- module, cls_name = cls.rsplit('.', 1)
+ module, cls_name = cls.rsplit(".", 1)
module = __import__(module)
cls = getattr(module, cls_name)
@@ -449,8 +559,10 @@ def get_instance(args):
def autoreload(args, excqueue=None):
- console.print(' --- AutoReload Mode: Monitoring `content`, `theme` and'
- ' `settings` for changes. ---')
+ console.print(
+ " --- AutoReload Mode: Monitoring `content`, `theme` and"
+ " `settings` for changes. ---"
+ )
pelican, settings = get_instance(args)
settings_file = os.path.abspath(args.settings)
while True:
@@ -463,8 +575,9 @@ def autoreload(args, excqueue=None):
if settings_file in changed_files:
pelican, settings = get_instance(args)
- console.print('\n-> Modified: {}. re-generating...'.format(
- ', '.join(changed_files)))
+ console.print(
+ "\n-> Modified: {}. re-generating...".format(", ".join(changed_files))
+ )
except KeyboardInterrupt:
if excqueue is not None:
@@ -473,15 +586,14 @@ def autoreload(args, excqueue=None):
raise
except Exception as e:
- if (args.verbosity == logging.DEBUG):
+ if args.verbosity == logging.DEBUG:
if excqueue is not None:
- excqueue.put(
- traceback.format_exception_only(type(e), e)[-1])
+ excqueue.put(traceback.format_exception_only(type(e), e)[-1])
else:
raise
logger.warning(
- 'Caught exception:\n"%s".', e,
- exc_info=settings.get('DEBUG', False))
+ 'Caught exception:\n"%s".', e, exc_info=settings.get("DEBUG", False)
+ )
def listen(server, port, output, excqueue=None):
@@ -491,8 +603,7 @@ def listen(server, port, output, excqueue=None):
RootedHTTPServer.allow_reuse_address = True
try:
- httpd = RootedHTTPServer(
- output, (server, port), ComplexHTTPRequestHandler)
+ httpd = RootedHTTPServer(output, (server, port), ComplexHTTPRequestHandler)
except OSError as e:
logging.error("Could not listen on port %s, server %s.", port, server)
if excqueue is not None:
@@ -500,8 +611,9 @@ def listen(server, port, output, excqueue=None):
return
try:
- console.print("Serving site at: http://{}:{} - Tap CTRL-C to stop".format(
- server, port))
+ console.print(
+ "Serving site at: http://{}:{} - Tap CTRL-C to stop".format(server, port)
+ )
httpd.serve_forever()
except Exception as e:
if excqueue is not None:
@@ -518,24 +630,31 @@ def listen(server, port, output, excqueue=None):
def main(argv=None):
args = parse_arguments(argv)
logs_dedup_min_level = getattr(logging, args.logs_dedup_min_level)
- init_logging(level=args.verbosity, fatal=args.fatal,
- name=__name__, logs_dedup_min_level=logs_dedup_min_level)
+ init_logging(
+ level=args.verbosity,
+ fatal=args.fatal,
+ name=__name__,
+ logs_dedup_min_level=logs_dedup_min_level,
+ )
- logger.debug('Pelican version: %s', __version__)
- logger.debug('Python version: %s', sys.version.split()[0])
+ logger.debug("Pelican version: %s", __version__)
+ logger.debug("Python version: %s", sys.version.split()[0])
try:
pelican, settings = get_instance(args)
if args.autoreload and args.listen:
excqueue = multiprocessing.Queue()
- p1 = multiprocessing.Process(
- target=autoreload,
- args=(args, excqueue))
+ p1 = multiprocessing.Process(target=autoreload, args=(args, excqueue))
p2 = multiprocessing.Process(
target=listen,
- args=(settings.get('BIND'), settings.get('PORT'),
- settings.get("OUTPUT_PATH"), excqueue))
+ args=(
+ settings.get("BIND"),
+ settings.get("PORT"),
+ settings.get("OUTPUT_PATH"),
+ excqueue,
+ ),
+ )
try:
p1.start()
p2.start()
@@ -548,16 +667,17 @@ def main(argv=None):
elif args.autoreload:
autoreload(args)
elif args.listen:
- listen(settings.get('BIND'), settings.get('PORT'),
- settings.get("OUTPUT_PATH"))
+ listen(
+ settings.get("BIND"), settings.get("PORT"), settings.get("OUTPUT_PATH")
+ )
else:
with console.status("Generating..."):
pelican.run()
except KeyboardInterrupt:
- logger.warning('Keyboard interrupt received. Exiting.')
+ logger.warning("Keyboard interrupt received. Exiting.")
except Exception as e:
logger.critical("%s: %s", e.__class__.__name__, e)
if args.verbosity == logging.DEBUG:
console.print_exception()
- sys.exit(getattr(e, 'exitcode', 1))
+ sys.exit(getattr(e, "exitcode", 1))
diff --git a/pelican/__main__.py b/pelican/__main__.py
index 69a5b95d..17aead3b 100644
--- a/pelican/__main__.py
+++ b/pelican/__main__.py
@@ -5,5 +5,5 @@ python -m pelican module entry point to run via python -m
from . import main
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/pelican/cache.py b/pelican/cache.py
index d2665691..d1f8550e 100644
--- a/pelican/cache.py
+++ b/pelican/cache.py
@@ -19,29 +19,35 @@ class FileDataCacher:
Sets caching policy according to *caching_policy*.
"""
self.settings = settings
- self._cache_path = os.path.join(self.settings['CACHE_PATH'],
- cache_name)
+ self._cache_path = os.path.join(self.settings["CACHE_PATH"], cache_name)
self._cache_data_policy = caching_policy
- if self.settings['GZIP_CACHE']:
+ if self.settings["GZIP_CACHE"]:
import gzip
+
self._cache_open = gzip.open
else:
self._cache_open = open
if load_policy:
try:
- with self._cache_open(self._cache_path, 'rb') as fhandle:
+ with self._cache_open(self._cache_path, "rb") as fhandle:
self._cache = pickle.load(fhandle)
except (OSError, UnicodeDecodeError) as err:
- logger.debug('Cannot load cache %s (this is normal on first '
- 'run). Proceeding with empty cache.\n%s',
- self._cache_path, err)
+ logger.debug(
+ "Cannot load cache %s (this is normal on first "
+ "run). Proceeding with empty cache.\n%s",
+ self._cache_path,
+ err,
+ )
self._cache = {}
except pickle.PickleError as err:
- logger.warning('Cannot unpickle cache %s, cache may be using '
- 'an incompatible protocol (see pelican '
- 'caching docs). '
- 'Proceeding with empty cache.\n%s',
- self._cache_path, err)
+ logger.warning(
+ "Cannot unpickle cache %s, cache may be using "
+ "an incompatible protocol (see pelican "
+ "caching docs). "
+ "Proceeding with empty cache.\n%s",
+ self._cache_path,
+ err,
+ )
self._cache = {}
else:
self._cache = {}
@@ -62,12 +68,13 @@ class FileDataCacher:
"""Save the updated cache"""
if self._cache_data_policy:
try:
- mkdir_p(self.settings['CACHE_PATH'])
- with self._cache_open(self._cache_path, 'wb') as fhandle:
+ mkdir_p(self.settings["CACHE_PATH"])
+ with self._cache_open(self._cache_path, "wb") as fhandle:
pickle.dump(self._cache, fhandle)
except (OSError, pickle.PicklingError, TypeError) as err:
- logger.warning('Could not save cache %s\n ... %s',
- self._cache_path, err)
+ logger.warning(
+ "Could not save cache %s\n ... %s", self._cache_path, err
+ )
class FileStampDataCacher(FileDataCacher):
@@ -80,8 +87,8 @@ class FileStampDataCacher(FileDataCacher):
super().__init__(settings, cache_name, caching_policy, load_policy)
- method = self.settings['CHECK_MODIFIED_METHOD']
- if method == 'mtime':
+ method = self.settings["CHECK_MODIFIED_METHOD"]
+ if method == "mtime":
self._filestamp_func = os.path.getmtime
else:
try:
@@ -89,12 +96,12 @@ class FileStampDataCacher(FileDataCacher):
def filestamp_func(filename):
"""return hash of file contents"""
- with open(filename, 'rb') as fhandle:
+ with open(filename, "rb") as fhandle:
return hash_func(fhandle.read()).digest()
self._filestamp_func = filestamp_func
except AttributeError as err:
- logger.warning('Could not get hashing function\n\t%s', err)
+ logger.warning("Could not get hashing function\n\t%s", err)
self._filestamp_func = None
def cache_data(self, filename, data):
@@ -115,9 +122,8 @@ class FileStampDataCacher(FileDataCacher):
try:
return self._filestamp_func(filename)
except (OSError, TypeError) as err:
- logger.warning('Cannot get modification stamp for %s\n\t%s',
- filename, err)
- return ''
+ logger.warning("Cannot get modification stamp for %s\n\t%s", filename, err)
+ return ""
def get_cached_data(self, filename, default=None):
"""Get the cached data for the given filename
diff --git a/pelican/contents.py b/pelican/contents.py
index c347a999..f99e6426 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -16,12 +16,19 @@ except ModuleNotFoundError:
from pelican.plugins import signals
from pelican.settings import DEFAULT_CONFIG
-from pelican.utils import (deprecated_attribute, memoized, path_to_url,
- posixize_path, sanitised_join, set_date_tzinfo,
- slugify, truncate_html_words)
+from pelican.utils import (
+ deprecated_attribute,
+ memoized,
+ path_to_url,
+ posixize_path,
+ sanitised_join,
+ set_date_tzinfo,
+ slugify,
+ truncate_html_words,
+)
# Import these so that they're available when you import from pelican.contents.
-from pelican.urlwrappers import (Author, Category, Tag, URLWrapper) # NOQA
+from pelican.urlwrappers import Author, Category, Tag, URLWrapper # NOQA
logger = logging.getLogger(__name__)
@@ -36,12 +43,14 @@ class Content:
:param context: The shared context between generators.
"""
- @deprecated_attribute(old='filename', new='source_path', since=(3, 2, 0))
+
+ @deprecated_attribute(old="filename", new="source_path", since=(3, 2, 0))
def filename():
return None
- def __init__(self, content, metadata=None, settings=None,
- source_path=None, context=None):
+ def __init__(
+ self, content, metadata=None, settings=None, source_path=None, context=None
+ ):
if metadata is None:
metadata = {}
if settings is None:
@@ -59,8 +68,8 @@ class Content:
# set metadata as attributes
for key, value in local_metadata.items():
- if key in ('save_as', 'url'):
- key = 'override_' + key
+ if key in ("save_as", "url"):
+ key = "override_" + key
setattr(self, key.lower(), value)
# also keep track of the metadata attributes available
@@ -71,53 +80,52 @@ class Content:
# First, read the authors from "authors", if not, fallback to "author"
# and if not use the settings defined one, if any.
- if not hasattr(self, 'author'):
- if hasattr(self, 'authors'):
+ if not hasattr(self, "author"):
+ if hasattr(self, "authors"):
self.author = self.authors[0]
- elif 'AUTHOR' in settings:
- self.author = Author(settings['AUTHOR'], settings)
+ elif "AUTHOR" in settings:
+ self.author = Author(settings["AUTHOR"], settings)
- if not hasattr(self, 'authors') and hasattr(self, 'author'):
+ if not hasattr(self, "authors") and hasattr(self, "author"):
self.authors = [self.author]
# XXX Split all the following code into pieces, there is too much here.
# manage languages
self.in_default_lang = True
- if 'DEFAULT_LANG' in settings:
- default_lang = settings['DEFAULT_LANG'].lower()
- if not hasattr(self, 'lang'):
+ if "DEFAULT_LANG" in settings:
+ default_lang = settings["DEFAULT_LANG"].lower()
+ if not hasattr(self, "lang"):
self.lang = default_lang
- self.in_default_lang = (self.lang == default_lang)
+ self.in_default_lang = self.lang == default_lang
# create the slug if not existing, generate slug according to
# setting of SLUG_ATTRIBUTE
- if not hasattr(self, 'slug'):
- if (settings['SLUGIFY_SOURCE'] == 'title' and
- hasattr(self, 'title')):
+ if not hasattr(self, "slug"):
+ if settings["SLUGIFY_SOURCE"] == "title" and hasattr(self, "title"):
value = self.title
- elif (settings['SLUGIFY_SOURCE'] == 'basename' and
- source_path is not None):
+ elif settings["SLUGIFY_SOURCE"] == "basename" and source_path is not None:
value = os.path.basename(os.path.splitext(source_path)[0])
else:
value = None
if value is not None:
self.slug = slugify(
value,
- regex_subs=settings.get('SLUG_REGEX_SUBSTITUTIONS', []),
- preserve_case=settings.get('SLUGIFY_PRESERVE_CASE', False),
- use_unicode=settings.get('SLUGIFY_USE_UNICODE', False))
+ regex_subs=settings.get("SLUG_REGEX_SUBSTITUTIONS", []),
+ preserve_case=settings.get("SLUGIFY_PRESERVE_CASE", False),
+ use_unicode=settings.get("SLUGIFY_USE_UNICODE", False),
+ )
self.source_path = source_path
self.relative_source_path = self.get_relative_source_path()
# manage the date format
- if not hasattr(self, 'date_format'):
- if hasattr(self, 'lang') and self.lang in settings['DATE_FORMATS']:
- self.date_format = settings['DATE_FORMATS'][self.lang]
+ if not hasattr(self, "date_format"):
+ if hasattr(self, "lang") and self.lang in settings["DATE_FORMATS"]:
+ self.date_format = settings["DATE_FORMATS"][self.lang]
else:
- self.date_format = settings['DEFAULT_DATE_FORMAT']
+ self.date_format = settings["DEFAULT_DATE_FORMAT"]
if isinstance(self.date_format, tuple):
locale_string = self.date_format[0]
@@ -129,22 +137,22 @@ class Content:
timezone = getattr(self, "timezone", default_timezone)
self.timezone = ZoneInfo(timezone)
- if hasattr(self, 'date'):
+ if hasattr(self, "date"):
self.date = set_date_tzinfo(self.date, timezone)
self.locale_date = self.date.strftime(self.date_format)
- if hasattr(self, 'modified'):
+ if hasattr(self, "modified"):
self.modified = set_date_tzinfo(self.modified, timezone)
self.locale_modified = self.modified.strftime(self.date_format)
# manage status
- if not hasattr(self, 'status'):
+ if not hasattr(self, "status"):
# Previous default of None broke comment plugins and perhaps others
- self.status = getattr(self, 'default_status', '')
+ self.status = getattr(self, "default_status", "")
# store the summary metadata if it is set
- if 'summary' in metadata:
- self._summary = metadata['summary']
+ if "summary" in metadata:
+ self._summary = metadata["summary"]
signals.content_object_init.send(self)
@@ -156,8 +164,8 @@ class Content:
for prop in self.mandatory_properties:
if not hasattr(self, prop):
logger.error(
- "Skipping %s: could not find information about '%s'",
- self, prop)
+ "Skipping %s: could not find information about '%s'", self, prop
+ )
return False
return True
@@ -183,12 +191,13 @@ class Content:
return True
def _has_valid_status(self):
- if hasattr(self, 'allowed_statuses'):
+ if hasattr(self, "allowed_statuses"):
if self.status not in self.allowed_statuses:
logger.error(
"Unknown status '%s' for file %s, skipping it. (Not in %s)",
self.status,
- self, self.allowed_statuses
+ self,
+ self.allowed_statuses,
)
return False
@@ -198,42 +207,48 @@ class Content:
def is_valid(self):
"""Validate Content"""
# Use all() to not short circuit and get results of all validations
- return all([self._has_valid_mandatory_properties(),
- self._has_valid_save_as(),
- self._has_valid_status()])
+ return all(
+ [
+ self._has_valid_mandatory_properties(),
+ self._has_valid_save_as(),
+ self._has_valid_status(),
+ ]
+ )
@property
def url_format(self):
"""Returns the URL, formatted with the proper values"""
metadata = copy.copy(self.metadata)
- path = self.metadata.get('path', self.get_relative_source_path())
- metadata.update({
- 'path': path_to_url(path),
- 'slug': getattr(self, 'slug', ''),
- 'lang': getattr(self, 'lang', 'en'),
- 'date': getattr(self, 'date', datetime.datetime.now()),
- 'author': self.author.slug if hasattr(self, 'author') else '',
- 'category': self.category.slug if hasattr(self, 'category') else ''
- })
+ path = self.metadata.get("path", self.get_relative_source_path())
+ metadata.update(
+ {
+ "path": path_to_url(path),
+ "slug": getattr(self, "slug", ""),
+ "lang": getattr(self, "lang", "en"),
+ "date": getattr(self, "date", datetime.datetime.now()),
+ "author": self.author.slug if hasattr(self, "author") else "",
+ "category": self.category.slug if hasattr(self, "category") else "",
+ }
+ )
return metadata
def _expand_settings(self, key, klass=None):
if not klass:
klass = self.__class__.__name__
- fq_key = ('{}_{}'.format(klass, key)).upper()
+ fq_key = ("{}_{}".format(klass, key)).upper()
return str(self.settings[fq_key]).format(**self.url_format)
def get_url_setting(self, key):
- if hasattr(self, 'override_' + key):
- return getattr(self, 'override_' + key)
- key = key if self.in_default_lang else 'lang_%s' % key
+ if hasattr(self, "override_" + key):
+ return getattr(self, "override_" + key)
+ key = key if self.in_default_lang else "lang_%s" % key
return self._expand_settings(key)
def _link_replacer(self, siteurl, m):
- what = m.group('what')
- value = urlparse(m.group('value'))
+ what = m.group("what")
+ value = urlparse(m.group("value"))
path = value.path
- origin = m.group('path')
+ origin = m.group("path")
# urllib.parse.urljoin() produces `a.html` for urljoin("..", "a.html")
# so if RELATIVE_URLS are enabled, we fall back to os.path.join() to
@@ -241,7 +256,7 @@ class Content:
# `baz/http://foo/bar.html` for join("baz", "http://foo/bar.html")
# instead of correct "http://foo/bar.html", so one has to pick a side
# as there is no silver bullet.
- if self.settings['RELATIVE_URLS']:
+ if self.settings["RELATIVE_URLS"]:
joiner = os.path.join
else:
joiner = urljoin
@@ -251,16 +266,17 @@ class Content:
# os.path.join()), so in order to get a correct answer one needs to
# append a trailing slash to siteurl in that case. This also makes
# the new behavior fully compatible with Pelican 3.7.1.
- if not siteurl.endswith('/'):
- siteurl += '/'
+ if not siteurl.endswith("/"):
+ siteurl += "/"
# XXX Put this in a different location.
- if what in {'filename', 'static', 'attach'}:
+ if what in {"filename", "static", "attach"}:
+
def _get_linked_content(key, url):
nonlocal value
def _find_path(path):
- if path.startswith('/'):
+ if path.startswith("/"):
path = path[1:]
else:
# relative to the source path of this content
@@ -287,59 +303,64 @@ class Content:
return result
# check if a static file is linked with {filename}
- if what == 'filename' and key == 'generated_content':
- linked_content = _get_linked_content('static_content', value)
+ if what == "filename" and key == "generated_content":
+ linked_content = _get_linked_content("static_content", value)
if linked_content:
logger.warning(
- '{filename} used for linking to static'
- ' content %s in %s. Use {static} instead',
+ "{filename} used for linking to static"
+ " content %s in %s. Use {static} instead",
value.path,
- self.get_relative_source_path())
+ self.get_relative_source_path(),
+ )
return linked_content
return None
- if what == 'filename':
- key = 'generated_content'
+ if what == "filename":
+ key = "generated_content"
else:
- key = 'static_content'
+ key = "static_content"
linked_content = _get_linked_content(key, value)
if linked_content:
- if what == 'attach':
+ if what == "attach":
linked_content.attach_to(self)
origin = joiner(siteurl, linked_content.url)
- origin = origin.replace('\\', '/') # for Windows paths.
+ origin = origin.replace("\\", "/") # for Windows paths.
else:
logger.warning(
"Unable to find '%s', skipping url replacement.",
- value.geturl(), extra={
- 'limit_msg': ("Other resources were not found "
- "and their urls not replaced")})
- elif what == 'category':
+ value.geturl(),
+ extra={
+ "limit_msg": (
+ "Other resources were not found "
+ "and their urls not replaced"
+ )
+ },
+ )
+ elif what == "category":
origin = joiner(siteurl, Category(path, self.settings).url)
- elif what == 'tag':
+ elif what == "tag":
origin = joiner(siteurl, Tag(path, self.settings).url)
- elif what == 'index':
- origin = joiner(siteurl, self.settings['INDEX_SAVE_AS'])
- elif what == 'author':
+ elif what == "index":
+ origin = joiner(siteurl, self.settings["INDEX_SAVE_AS"])
+ elif what == "author":
origin = joiner(siteurl, Author(path, self.settings).url)
else:
logger.warning(
- "Replacement Indicator '%s' not recognized, "
- "skipping replacement",
- what)
+ "Replacement Indicator '%s' not recognized, " "skipping replacement",
+ what,
+ )
# keep all other parts, such as query, fragment, etc.
parts = list(value)
parts[2] = origin
origin = urlunparse(parts)
- return ''.join((m.group('markup'), m.group('quote'), origin,
- m.group('quote')))
+ return "".join((m.group("markup"), m.group("quote"), origin, m.group("quote")))
def _get_intrasite_link_regex(self):
- intrasite_link_regex = self.settings['INTRASITE_LINK_REGEX']
+ intrasite_link_regex = self.settings["INTRASITE_LINK_REGEX"]
regex = r"""
(?P
@@ -21,25 +20,24 @@ TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False)
class TestBase(LoggedTestCase):
-
def setUp(self):
super().setUp()
self.old_locale = locale.setlocale(locale.LC_ALL)
- locale.setlocale(locale.LC_ALL, 'C')
+ locale.setlocale(locale.LC_ALL, "C")
self.page_kwargs = {
- 'content': TEST_CONTENT,
- 'context': {
- 'localsiteurl': '',
- 'generated_content': {},
- 'static_content': {},
- 'static_links': set()
+ "content": TEST_CONTENT,
+ "context": {
+ "localsiteurl": "",
+ "generated_content": {},
+ "static_content": {},
+ "static_links": set(),
},
- 'metadata': {
- 'summary': TEST_SUMMARY,
- 'title': 'foo bar',
- 'author': Author('Blogger', DEFAULT_CONFIG),
+ "metadata": {
+ "summary": TEST_SUMMARY,
+ "title": "foo bar",
+ "author": Author("Blogger", DEFAULT_CONFIG),
},
- 'source_path': '/path/to/file/foo.ext'
+ "source_path": "/path/to/file/foo.ext",
}
self._disable_limit_filter()
@@ -49,10 +47,12 @@ class TestBase(LoggedTestCase):
def _disable_limit_filter(self):
from pelican.contents import logger
+
logger.disable_filter()
def _enable_limit_filter(self):
from pelican.contents import logger
+
logger.enable_filter()
def _copy_page_kwargs(self):
@@ -72,9 +72,12 @@ class TestPage(TestBase):
def test_use_args(self):
# Creating a page with arguments passed to the constructor should use
# them to initialise object's attributes.
- metadata = {'foo': 'bar', 'foobar': 'baz', 'title': 'foobar', }
- page = Page(TEST_CONTENT, metadata=metadata,
- context={'localsiteurl': ''})
+ metadata = {
+ "foo": "bar",
+ "foobar": "baz",
+ "title": "foobar",
+ }
+ page = Page(TEST_CONTENT, metadata=metadata, context={"localsiteurl": ""})
for key, value in metadata.items():
self.assertTrue(hasattr(page, key))
self.assertEqual(value, getattr(page, key))
@@ -82,13 +85,14 @@ class TestPage(TestBase):
def test_mandatory_properties(self):
# If the title is not set, must throw an exception.
- page = Page('content')
+ page = Page("content")
self.assertFalse(page._has_valid_mandatory_properties())
self.assertLogCountEqual(
- count=1,
- msg="Skipping .*: could not find information about 'title'",
- level=logging.ERROR)
- page = Page('content', metadata={'title': 'foobar'})
+ count=1,
+ msg="Skipping .*: could not find information about 'title'",
+ level=logging.ERROR,
+ )
+ page = Page("content", metadata={"title": "foobar"})
self.assertTrue(page._has_valid_mandatory_properties())
def test_summary_from_metadata(self):
@@ -101,31 +105,32 @@ class TestPage(TestBase):
# generated summary should not exceed the given length.
page_kwargs = self._copy_page_kwargs()
settings = get_settings()
- page_kwargs['settings'] = settings
- del page_kwargs['metadata']['summary']
- settings['SUMMARY_MAX_LENGTH'] = None
+ page_kwargs["settings"] = settings
+ del page_kwargs["metadata"]["summary"]
+ settings["SUMMARY_MAX_LENGTH"] = None
page = Page(**page_kwargs)
self.assertEqual(page.summary, TEST_CONTENT)
- settings['SUMMARY_MAX_LENGTH'] = 10
+ settings["SUMMARY_MAX_LENGTH"] = 10
page = Page(**page_kwargs)
self.assertEqual(page.summary, truncate_html_words(TEST_CONTENT, 10))
- settings['SUMMARY_MAX_LENGTH'] = 0
+ settings["SUMMARY_MAX_LENGTH"] = 0
page = Page(**page_kwargs)
- self.assertEqual(page.summary, '')
+ self.assertEqual(page.summary, "")
def test_summary_end_suffix(self):
# If a :SUMMARY_END_SUFFIX: is set, and there is no other summary,
# generated summary should contain the specified marker at the end.
page_kwargs = self._copy_page_kwargs()
settings = get_settings()
- page_kwargs['settings'] = settings
- del page_kwargs['metadata']['summary']
- settings['SUMMARY_END_SUFFIX'] = 'test_marker'
- settings['SUMMARY_MAX_LENGTH'] = 10
+ page_kwargs["settings"] = settings
+ del page_kwargs["metadata"]["summary"]
+ settings["SUMMARY_END_SUFFIX"] = "test_marker"
+ settings["SUMMARY_MAX_LENGTH"] = 10
page = Page(**page_kwargs)
- self.assertEqual(page.summary, truncate_html_words(TEST_CONTENT, 10,
- 'test_marker'))
- self.assertIn('test_marker', page.summary)
+ self.assertEqual(
+ page.summary, truncate_html_words(TEST_CONTENT, 10, "test_marker")
+ )
+ self.assertIn("test_marker", page.summary)
def test_summary_get_summary_warning(self):
"""calling ._get_summary() should issue a warning"""
@@ -134,57 +139,61 @@ class TestPage(TestBase):
self.assertEqual(page.summary, TEST_SUMMARY)
self.assertEqual(page._get_summary(), TEST_SUMMARY)
self.assertLogCountEqual(
- count=1,
- msg=r"_get_summary\(\) has been deprecated since 3\.6\.4\. "
- "Use the summary decorator instead",
- level=logging.WARNING)
+ count=1,
+ msg=r"_get_summary\(\) has been deprecated since 3\.6\.4\. "
+ "Use the summary decorator instead",
+ level=logging.WARNING,
+ )
def test_slug(self):
page_kwargs = self._copy_page_kwargs()
settings = get_settings()
- page_kwargs['settings'] = settings
- settings['SLUGIFY_SOURCE'] = "title"
+ page_kwargs["settings"] = settings
+ settings["SLUGIFY_SOURCE"] = "title"
page = Page(**page_kwargs)
- self.assertEqual(page.slug, 'foo-bar')
- settings['SLUGIFY_SOURCE'] = "basename"
+ self.assertEqual(page.slug, "foo-bar")
+ settings["SLUGIFY_SOURCE"] = "basename"
page = Page(**page_kwargs)
- self.assertEqual(page.slug, 'foo')
+ self.assertEqual(page.slug, "foo")
# test slug from title with unicode and case
inputs = (
# (title, expected, preserve_case, use_unicode)
- ('指導書', 'zhi-dao-shu', False, False),
- ('指導書', 'Zhi-Dao-Shu', True, False),
- ('指導書', '指導書', False, True),
- ('指導書', '指導書', True, True),
- ('Çığ', 'cig', False, False),
- ('Çığ', 'Cig', True, False),
- ('Çığ', 'çığ', False, True),
- ('Çığ', 'Çığ', True, True),
+ ("指導書", "zhi-dao-shu", False, False),
+ ("指導書", "Zhi-Dao-Shu", True, False),
+ ("指導書", "指導書", False, True),
+ ("指導書", "指導書", True, True),
+ ("Çığ", "cig", False, False),
+ ("Çığ", "Cig", True, False),
+ ("Çığ", "çığ", False, True),
+ ("Çığ", "Çığ", True, True),
)
settings = get_settings()
page_kwargs = self._copy_page_kwargs()
- page_kwargs['settings'] = settings
+ page_kwargs["settings"] = settings
for title, expected, preserve_case, use_unicode in inputs:
- settings['SLUGIFY_PRESERVE_CASE'] = preserve_case
- settings['SLUGIFY_USE_UNICODE'] = use_unicode
- page_kwargs['metadata']['title'] = title
+ settings["SLUGIFY_PRESERVE_CASE"] = preserve_case
+ settings["SLUGIFY_USE_UNICODE"] = use_unicode
+ page_kwargs["metadata"]["title"] = title
page = Page(**page_kwargs)
- self.assertEqual(page.slug, expected,
- (title, preserve_case, use_unicode))
+ self.assertEqual(page.slug, expected, (title, preserve_case, use_unicode))
def test_defaultlang(self):
# If no lang is given, default to the default one.
page = Page(**self.page_kwargs)
- self.assertEqual(page.lang, DEFAULT_CONFIG['DEFAULT_LANG'])
+ self.assertEqual(page.lang, DEFAULT_CONFIG["DEFAULT_LANG"])
# it is possible to specify the lang in the metadata infos
- self.page_kwargs['metadata'].update({'lang': 'fr', })
+ self.page_kwargs["metadata"].update(
+ {
+ "lang": "fr",
+ }
+ )
page = Page(**self.page_kwargs)
- self.assertEqual(page.lang, 'fr')
+ self.assertEqual(page.lang, "fr")
def test_save_as(self):
# If a lang is not the default lang, save_as should be set
@@ -195,7 +204,11 @@ class TestPage(TestBase):
self.assertEqual(page.save_as, "pages/foo-bar.html")
# if a language is defined, save_as should include it accordingly
- self.page_kwargs['metadata'].update({'lang': 'fr', })
+ self.page_kwargs["metadata"].update(
+ {
+ "lang": "fr",
+ }
+ )
page = Page(**self.page_kwargs)
self.assertEqual(page.save_as, "pages/foo-bar-fr.html")
@@ -206,34 +219,32 @@ class TestPage(TestBase):
# If 'source_path' is None, 'relative_source_path' should
# also return None
- page_kwargs['source_path'] = None
+ page_kwargs["source_path"] = None
page = Page(**page_kwargs)
self.assertIsNone(page.relative_source_path)
page_kwargs = self._copy_page_kwargs()
settings = get_settings()
- full_path = page_kwargs['source_path']
+ full_path = page_kwargs["source_path"]
- settings['PATH'] = os.path.dirname(full_path)
- page_kwargs['settings'] = settings
+ settings["PATH"] = os.path.dirname(full_path)
+ page_kwargs["settings"] = settings
page = Page(**page_kwargs)
# if 'source_path' is set, 'relative_source_path' should
# return the relative path from 'PATH' to 'source_path'
self.assertEqual(
page.relative_source_path,
- os.path.relpath(
- full_path,
- os.path.dirname(full_path)
- ))
+ os.path.relpath(full_path, os.path.dirname(full_path)),
+ )
def test_metadata_url_format(self):
# Arbitrary metadata should be passed through url_format()
page = Page(**self.page_kwargs)
- self.assertIn('summary', page.url_format.keys())
- page.metadata['directory'] = 'test-dir'
- page.settings = get_settings(PAGE_SAVE_AS='{directory}/{slug}')
- self.assertEqual(page.save_as, 'test-dir/foo-bar')
+ self.assertIn("summary", page.url_format.keys())
+ page.metadata["directory"] = "test-dir"
+ page.settings = get_settings(PAGE_SAVE_AS="{directory}/{slug}")
+ self.assertEqual(page.save_as, "test-dir/foo-bar")
def test_datetime(self):
# If DATETIME is set to a tuple, it should be used to override LOCALE
@@ -242,28 +253,28 @@ class TestPage(TestBase):
page_kwargs = self._copy_page_kwargs()
# set its date to dt
- page_kwargs['metadata']['date'] = dt
+ page_kwargs["metadata"]["date"] = dt
page = Page(**page_kwargs)
# page.locale_date is a unicode string in both python2 and python3
- dt_date = dt.strftime(DEFAULT_CONFIG['DEFAULT_DATE_FORMAT'])
+ dt_date = dt.strftime(DEFAULT_CONFIG["DEFAULT_DATE_FORMAT"])
self.assertEqual(page.locale_date, dt_date)
- page_kwargs['settings'] = get_settings()
+ page_kwargs["settings"] = get_settings()
# I doubt this can work on all platforms ...
if platform == "win32":
- locale = 'jpn'
+ locale = "jpn"
else:
- locale = 'ja_JP.utf8'
- page_kwargs['settings']['DATE_FORMATS'] = {'jp': (locale,
- '%Y-%m-%d(%a)')}
- page_kwargs['metadata']['lang'] = 'jp'
+ locale = "ja_JP.utf8"
+ page_kwargs["settings"]["DATE_FORMATS"] = {"jp": (locale, "%Y-%m-%d(%a)")}
+ page_kwargs["metadata"]["lang"] = "jp"
import locale as locale_module
+
try:
page = Page(**page_kwargs)
- self.assertEqual(page.locale_date, '2015-09-13(\u65e5)')
+ self.assertEqual(page.locale_date, "2015-09-13(\u65e5)")
except locale_module.Error:
# The constructor of ``Page`` will try to set the locale to
# ``ja_JP.utf8``. But this attempt will failed when there is no
@@ -277,22 +288,21 @@ class TestPage(TestBase):
def test_template(self):
# Pages default to page, metadata overwrites
default_page = Page(**self.page_kwargs)
- self.assertEqual('page', default_page.template)
+ self.assertEqual("page", default_page.template)
page_kwargs = self._copy_page_kwargs()
- page_kwargs['metadata']['template'] = 'custom'
+ page_kwargs["metadata"]["template"] = "custom"
custom_page = Page(**page_kwargs)
- self.assertEqual('custom', custom_page.template)
+ self.assertEqual("custom", custom_page.template)
def test_signal(self):
def receiver_test_function(sender):
receiver_test_function.has_been_called = True
pass
+
receiver_test_function.has_been_called = False
content_object_init.connect(receiver_test_function)
- self.assertIn(
- receiver_test_function,
- content_object_init.receivers_for(Page))
+ self.assertIn(receiver_test_function, content_object_init.receivers_for(Page))
self.assertFalse(receiver_test_function.has_been_called)
Page(**self.page_kwargs)
@@ -303,102 +313,106 @@ class TestPage(TestBase):
# filenames, tags and categories.
settings = get_settings()
args = self.page_kwargs.copy()
- args['settings'] = settings
+ args["settings"] = settings
# Tag
- args['content'] = ('A simple test, with a '
- 'link')
+ args["content"] = "A simple test, with a " 'link'
page = Page(**args)
- content = page.get_content('http://notmyidea.org')
+ content = page.get_content("http://notmyidea.org")
self.assertEqual(
content,
- ('A simple test, with a '
- 'link'))
+ (
+ "A simple test, with a "
+ 'link'
+ ),
+ )
# Category
- args['content'] = ('A simple test, with a '
- 'link')
+ args["content"] = (
+ "A simple test, with a " 'link'
+ )
page = Page(**args)
- content = page.get_content('http://notmyidea.org')
+ content = page.get_content("http://notmyidea.org")
self.assertEqual(
content,
- ('A simple test, with a '
- 'link'))
+ (
+ "A simple test, with a "
+ 'link'
+ ),
+ )
def test_intrasite_link(self):
- cls_name = '_DummyArticle'
- article = type(cls_name, (object,), {'url': 'article.html'})
+ cls_name = "_DummyArticle"
+ article = type(cls_name, (object,), {"url": "article.html"})
args = self.page_kwargs.copy()
- args['settings'] = get_settings()
- args['source_path'] = 'content'
- args['context']['generated_content'] = {'article.rst': article}
+ args["settings"] = get_settings()
+ args["source_path"] = "content"
+ args["context"]["generated_content"] = {"article.rst": article}
# Classic intrasite link via filename
- args['content'] = (
- 'A simple test, with a '
- 'link'
+ args["content"] = (
+ "A simple test, with a " 'link'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'A simple test, with a '
- 'link'
+ "A simple test, with a "
+ 'link',
)
# fragment
- args['content'] = (
- 'A simple test, with a '
+ args["content"] = (
+ "A simple test, with a "
'link'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'A simple test, with a '
- 'link'
+ "A simple test, with a "
+ 'link',
)
# query
- args['content'] = (
- 'A simple test, with a '
+ args["content"] = (
+ "A simple test, with a "
'link'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'A simple test, with a '
+ "A simple test, with a "
'link'
+ '?utm_whatever=234&highlight=word">link',
)
# combination
- args['content'] = (
- 'A simple test, with a '
+ args["content"] = (
+ "A simple test, with a "
'link'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'A simple test, with a '
+ "A simple test, with a "
'link'
+ '?utm_whatever=234&highlight=word#section-2">link',
)
# also test for summary in metadata
parsed = (
- 'A simple summary test, with a '
- 'link'
+ "A simple summary test, with a " 'link'
)
linked = (
- 'A simple summary test, with a '
+ "A simple summary test, with a "
'link'
)
- args['settings']['FORMATTED_FIELDS'] = ['summary', 'custom']
- args['metadata']['summary'] = parsed
- args['metadata']['custom'] = parsed
- args['context']['localsiteurl'] = 'http://notmyidea.org'
+ args["settings"]["FORMATTED_FIELDS"] = ["summary", "custom"]
+ args["metadata"]["summary"] = parsed
+ args["metadata"]["custom"] = parsed
+ args["context"]["localsiteurl"] = "http://notmyidea.org"
p = Page(**args)
# This is called implicitly from all generators and Pelican.run() once
# all files are processed. Here we process just one page so it needs
@@ -408,252 +422,236 @@ class TestPage(TestBase):
self.assertEqual(p.custom, linked)
def test_intrasite_link_more(self):
- cls_name = '_DummyAsset'
+ cls_name = "_DummyAsset"
args = self.page_kwargs.copy()
- args['settings'] = get_settings()
- args['source_path'] = 'content'
- args['context']['static_content'] = {
- 'images/poster.jpg':
- type(cls_name, (object,), {'url': 'images/poster.jpg'}),
- 'assets/video.mp4':
- type(cls_name, (object,), {'url': 'assets/video.mp4'}),
- 'images/graph.svg':
- type(cls_name, (object,), {'url': 'images/graph.svg'}),
+ args["settings"] = get_settings()
+ args["source_path"] = "content"
+ args["context"]["static_content"] = {
+ "images/poster.jpg": type(
+ cls_name, (object,), {"url": "images/poster.jpg"}
+ ),
+ "assets/video.mp4": type(cls_name, (object,), {"url": "assets/video.mp4"}),
+ "images/graph.svg": type(cls_name, (object,), {"url": "images/graph.svg"}),
}
- args['context']['generated_content'] = {
- 'reference.rst':
- type(cls_name, (object,), {'url': 'reference.html'}),
+ args["context"]["generated_content"] = {
+ "reference.rst": type(cls_name, (object,), {"url": "reference.html"}),
}
# video.poster
- args['content'] = (
- 'There is a video with poster '
+ args["content"] = (
+ "There is a video with poster "
''
+ ""
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'There is a video with poster '
+ "There is a video with poster "
''
+ "",
)
# object.data
- args['content'] = (
- 'There is a svg object '
+ args["content"] = (
+ "There is a svg object "
''
+ ""
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'There is a svg object '
+ "There is a svg object "
''
+ "",
)
# blockquote.cite
- args['content'] = (
- 'There is a blockquote with cite attribute '
+ args["content"] = (
+ "There is a blockquote with cite attribute "
']*
src=(['"])(.*?)\5
)
- """, re.X)
+ """,
+ re.X,
+ )
for match in re.findall(imgs, content):
logger.warning(
- 'Empty alt attribute for image %s in %s',
- os.path.basename(match[1] + match[5]), path,
- extra={'limit_msg': 'Other images have empty alt attributes'})
+ "Empty alt attribute for image %s in %s",
+ os.path.basename(match[1] + match[5]),
+ path,
+ extra={"limit_msg": "Other images have empty alt attributes"},
+ )
def default_metadata(settings=None, process=None):
metadata = {}
if settings:
- for name, value in dict(settings.get('DEFAULT_METADATA', {})).items():
+ for name, value in dict(settings.get("DEFAULT_METADATA", {})).items():
if process:
value = process(name, value)
metadata[name] = value
- if 'DEFAULT_CATEGORY' in settings:
- value = settings['DEFAULT_CATEGORY']
+ if "DEFAULT_CATEGORY" in settings:
+ value = settings["DEFAULT_CATEGORY"]
if process:
- value = process('category', value)
- metadata['category'] = value
- if settings.get('DEFAULT_DATE', None) and \
- settings['DEFAULT_DATE'] != 'fs':
- if isinstance(settings['DEFAULT_DATE'], str):
- metadata['date'] = get_date(settings['DEFAULT_DATE'])
+ value = process("category", value)
+ metadata["category"] = value
+ if settings.get("DEFAULT_DATE", None) and settings["DEFAULT_DATE"] != "fs":
+ if isinstance(settings["DEFAULT_DATE"], str):
+ metadata["date"] = get_date(settings["DEFAULT_DATE"])
else:
- metadata['date'] = datetime.datetime(*settings['DEFAULT_DATE'])
+ metadata["date"] = datetime.datetime(*settings["DEFAULT_DATE"])
return metadata
def path_metadata(full_path, source_path, settings=None):
metadata = {}
if settings:
- if settings.get('DEFAULT_DATE', None) == 'fs':
- metadata['date'] = datetime.datetime.fromtimestamp(
- os.stat(full_path).st_mtime)
- metadata['modified'] = metadata['date']
+ if settings.get("DEFAULT_DATE", None) == "fs":
+ metadata["date"] = datetime.datetime.fromtimestamp(
+ os.stat(full_path).st_mtime
+ )
+ metadata["modified"] = metadata["date"]
# Apply EXTRA_PATH_METADATA for the source path and the paths of any
# parent directories. Sorting EPM first ensures that the most specific
# path wins conflicts.
- epm = settings.get('EXTRA_PATH_METADATA', {})
+ epm = settings.get("EXTRA_PATH_METADATA", {})
for path, meta in sorted(epm.items()):
# Enforce a trailing slash when checking for parent directories.
# This prevents false positives when one file or directory's name
# is a prefix of another's.
- dirpath = posixize_path(os.path.join(path, ''))
+ dirpath = posixize_path(os.path.join(path, ""))
if source_path == path or source_path.startswith(dirpath):
metadata.update(meta)
@@ -736,11 +761,10 @@ def parse_path_metadata(source_path, settings=None, process=None):
subdir = os.path.basename(dirname)
if settings:
checks = []
- for key, data in [('FILENAME_METADATA', base),
- ('PATH_METADATA', source_path)]:
+ for key, data in [("FILENAME_METADATA", base), ("PATH_METADATA", source_path)]:
checks.append((settings.get(key, None), data))
- if settings.get('USE_FOLDER_AS_CATEGORY', None):
- checks.append(('(?P
blah blah
'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'There is a blockquote with cite attribute '
+ "There is a blockquote with cite attribute "
''
- 'blah blah'
- '
'
+ "blah blah"
+ "",
)
def test_intrasite_link_absolute(self):
"""Test that absolute URLs are merged properly."""
args = self.page_kwargs.copy()
- args['settings'] = get_settings(
- STATIC_URL='http://static.cool.site/{path}',
- ARTICLE_URL='http://blog.cool.site/{slug}.html')
- args['source_path'] = 'content'
- args['context']['static_content'] = {
- 'images/poster.jpg':
- Static('', settings=args['settings'],
- source_path='images/poster.jpg'),
+ args["settings"] = get_settings(
+ STATIC_URL="http://static.cool.site/{path}",
+ ARTICLE_URL="http://blog.cool.site/{slug}.html",
+ )
+ args["source_path"] = "content"
+ args["context"]["static_content"] = {
+ "images/poster.jpg": Static(
+ "", settings=args["settings"], source_path="images/poster.jpg"
+ ),
}
- args['context']['generated_content'] = {
- 'article.rst':
- Article('', settings=args['settings'], metadata={
- 'slug': 'article', 'title': 'Article'})
+ args["context"]["generated_content"] = {
+ "article.rst": Article(
+ "",
+ settings=args["settings"],
+ metadata={"slug": "article", "title": "Article"},
+ )
}
# Article link will go to blog
- args['content'] = (
- 'Article'
- )
- content = Page(**args).get_content('http://cool.site')
+ args["content"] = 'Article'
+ content = Page(**args).get_content("http://cool.site")
self.assertEqual(
- content,
- 'Article'
+ content, 'Article'
)
# Page link will go to the main site
- args['content'] = (
- 'Index'
- )
- content = Page(**args).get_content('http://cool.site')
+ args["content"] = 'Index'
+ content = Page(**args).get_content("http://cool.site")
+ self.assertEqual(content, 'Index')
+
+ # Image link will go to static
+ args["content"] = '
'
+ content = Page(**args).get_content("http://cool.site")
self.assertEqual(
- content,
- 'Index'
+ content, '
'
)
# Image link will go to static
- args['content'] = (
- '
'
- )
- content = Page(**args).get_content('http://cool.site')
+ args["content"] = ''
+ content = Page(**args).get_content("http://cool.site")
self.assertEqual(
- content,
- '
'
- )
-
- # Image link will go to static
- args['content'] = (
- ''
- )
- content = Page(**args).get_content('http://cool.site')
- self.assertEqual(
- content,
- ''
+ content, ''
)
def test_intrasite_link_escape(self):
- article = type(
- '_DummyArticle', (object,), {'url': 'article-spaces.html'})
- asset = type(
- '_DummyAsset', (object,), {'url': 'name@example.com'})
+ article = type("_DummyArticle", (object,), {"url": "article-spaces.html"})
+ asset = type("_DummyAsset", (object,), {"url": "name@example.com"})
args = self.page_kwargs.copy()
- args['settings'] = get_settings()
- args['source_path'] = 'content'
- args['context']['generated_content'] = {'article spaces.rst': article}
- args['context']['static_content'] = {'name@example.com': asset}
+ args["settings"] = get_settings()
+ args["source_path"] = "content"
+ args["context"]["generated_content"] = {"article spaces.rst": article}
+ args["context"]["static_content"] = {"name@example.com": asset}
expected_output = (
- 'A simple test with a '
+ "A simple test with a "
'link '
'file'
)
# not escaped
- args['content'] = (
- 'A simple test with a '
+ args["content"] = (
+ "A simple test with a "
'link '
'file'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(content, expected_output)
# html escaped
- args['content'] = (
- 'A simple test with a '
+ args["content"] = (
+ "A simple test with a "
'link '
'file'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(content, expected_output)
# url escaped
- args['content'] = (
- 'A simple test with a '
+ args["content"] = (
+ "A simple test with a "
'link '
'file'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(content, expected_output)
# html and url escaped
- args['content'] = (
- 'A simple test with a '
+ args["content"] = (
+ "A simple test with a "
'link '
'file'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(content, expected_output)
def test_intrasite_link_markdown_spaces(self):
- cls_name = '_DummyArticle'
- article = type(cls_name, (object,), {'url': 'article-spaces.html'})
+ cls_name = "_DummyArticle"
+ article = type(cls_name, (object,), {"url": "article-spaces.html"})
args = self.page_kwargs.copy()
- args['settings'] = get_settings()
- args['source_path'] = 'content'
- args['context']['generated_content'] = {'article spaces.rst': article}
+ args["settings"] = get_settings()
+ args["source_path"] = "content"
+ args["context"]["generated_content"] = {"article spaces.rst": article}
# An intrasite link via filename with %20 as a space
- args['content'] = (
- 'A simple test, with a '
- 'link'
+ args["content"] = (
+ "A simple test, with a " 'link'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'A simple test, with a '
- 'link'
+ "A simple test, with a "
+ 'link',
)
def test_intrasite_link_source_and_generated(self):
- """Test linking both to the source and the generated article
- """
- cls_name = '_DummyAsset'
+ """Test linking both to the source and the generated article"""
+ cls_name = "_DummyAsset"
args = self.page_kwargs.copy()
- args['settings'] = get_settings()
- args['source_path'] = 'content'
- args['context']['generated_content'] = {
- 'article.rst': type(cls_name, (object,), {'url': 'article.html'})}
- args['context']['static_content'] = {
- 'article.rst': type(cls_name, (object,), {'url': 'article.rst'})}
+ args["settings"] = get_settings()
+ args["source_path"] = "content"
+ args["context"]["generated_content"] = {
+ "article.rst": type(cls_name, (object,), {"url": "article.html"})
+ }
+ args["context"]["static_content"] = {
+ "article.rst": type(cls_name, (object,), {"url": "article.rst"})
+ }
- args['content'] = (
- 'A simple test, with a link to an'
+ args["content"] = (
+ "A simple test, with a link to an"
'article and its'
'source'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'A simple test, with a link to an'
+ "A simple test, with a link to an"
'article and its'
- 'source'
+ 'source',
)
def test_intrasite_link_to_static_content_with_filename(self):
- """Test linking to a static resource with deprecated {filename}
- """
- cls_name = '_DummyAsset'
+ """Test linking to a static resource with deprecated {filename}"""
+ cls_name = "_DummyAsset"
args = self.page_kwargs.copy()
- args['settings'] = get_settings()
- args['source_path'] = 'content'
- args['context']['static_content'] = {
- 'poster.jpg':
- type(cls_name, (object,), {'url': 'images/poster.jpg'})}
+ args["settings"] = get_settings()
+ args["source_path"] = "content"
+ args["context"]["static_content"] = {
+ "poster.jpg": type(cls_name, (object,), {"url": "images/poster.jpg"})
+ }
- args['content'] = (
- 'A simple test, with a link to a'
+ args["content"] = (
+ "A simple test, with a link to a"
'poster'
)
- content = Page(**args).get_content('http://notmyidea.org')
+ content = Page(**args).get_content("http://notmyidea.org")
self.assertEqual(
content,
- 'A simple test, with a link to a'
- 'poster'
+ "A simple test, with a link to a"
+ 'poster',
)
def test_multiple_authors(self):
@@ -661,9 +659,11 @@ class TestPage(TestBase):
args = self.page_kwargs.copy()
content = Page(**args)
assert content.authors == [content.author]
- args['metadata'].pop('author')
- args['metadata']['authors'] = [Author('First Author', DEFAULT_CONFIG),
- Author('Second Author', DEFAULT_CONFIG)]
+ args["metadata"].pop("author")
+ args["metadata"]["authors"] = [
+ Author("First Author", DEFAULT_CONFIG),
+ Author("Second Author", DEFAULT_CONFIG),
+ ]
content = Page(**args)
assert content.authors
assert content.author == content.authors[0]
@@ -673,173 +673,184 @@ class TestArticle(TestBase):
def test_template(self):
# Articles default to article, metadata overwrites
default_article = Article(**self.page_kwargs)
- self.assertEqual('article', default_article.template)
+ self.assertEqual("article", default_article.template)
article_kwargs = self._copy_page_kwargs()
- article_kwargs['metadata']['template'] = 'custom'
+ article_kwargs["metadata"]["template"] = "custom"
custom_article = Article(**article_kwargs)
- self.assertEqual('custom', custom_article.template)
+ self.assertEqual("custom", custom_article.template)
def test_slugify_category_author(self):
settings = get_settings()
- settings['SLUG_REGEX_SUBSTITUTIONS'] = [
- (r'C#', 'csharp'),
- (r'[^\w\s-]', ''),
- (r'(?u)\A\s*', ''),
- (r'(?u)\s*\Z', ''),
- (r'[-\s]+', '-'),
+ settings["SLUG_REGEX_SUBSTITUTIONS"] = [
+ (r"C#", "csharp"),
+ (r"[^\w\s-]", ""),
+ (r"(?u)\A\s*", ""),
+ (r"(?u)\s*\Z", ""),
+ (r"[-\s]+", "-"),
]
- settings['ARTICLE_URL'] = '{author}/{category}/{slug}/'
- settings['ARTICLE_SAVE_AS'] = '{author}/{category}/{slug}/index.html'
+ settings["ARTICLE_URL"] = "{author}/{category}/{slug}/"
+ settings["ARTICLE_SAVE_AS"] = "{author}/{category}/{slug}/index.html"
article_kwargs = self._copy_page_kwargs()
- article_kwargs['metadata']['author'] = Author("O'Brien", settings)
- article_kwargs['metadata']['category'] = Category(
- 'C# & stuff', settings)
- article_kwargs['metadata']['title'] = 'fnord'
- article_kwargs['settings'] = settings
+ article_kwargs["metadata"]["author"] = Author("O'Brien", settings)
+ article_kwargs["metadata"]["category"] = Category("C# & stuff", settings)
+ article_kwargs["metadata"]["title"] = "fnord"
+ article_kwargs["settings"] = settings
article = Article(**article_kwargs)
- self.assertEqual(article.url, 'obrien/csharp-stuff/fnord/')
- self.assertEqual(
- article.save_as, 'obrien/csharp-stuff/fnord/index.html')
+ self.assertEqual(article.url, "obrien/csharp-stuff/fnord/")
+ self.assertEqual(article.save_as, "obrien/csharp-stuff/fnord/index.html")
def test_slugify_with_author_substitutions(self):
settings = get_settings()
- settings['AUTHOR_REGEX_SUBSTITUTIONS'] = [
- ('Alexander Todorov', 'atodorov'),
- ('Krasimir Tsonev', 'krasimir'),
- (r'[^\w\s-]', ''),
- (r'(?u)\A\s*', ''),
- (r'(?u)\s*\Z', ''),
- (r'[-\s]+', '-'),
+ settings["AUTHOR_REGEX_SUBSTITUTIONS"] = [
+ ("Alexander Todorov", "atodorov"),
+ ("Krasimir Tsonev", "krasimir"),
+ (r"[^\w\s-]", ""),
+ (r"(?u)\A\s*", ""),
+ (r"(?u)\s*\Z", ""),
+ (r"[-\s]+", "-"),
]
- settings['ARTICLE_URL'] = 'blog/{author}/{slug}/'
- settings['ARTICLE_SAVE_AS'] = 'blog/{author}/{slug}/index.html'
+ settings["ARTICLE_URL"] = "blog/{author}/{slug}/"
+ settings["ARTICLE_SAVE_AS"] = "blog/{author}/{slug}/index.html"
article_kwargs = self._copy_page_kwargs()
- article_kwargs['metadata']['author'] = Author('Alexander Todorov',
- settings)
- article_kwargs['metadata']['title'] = 'fnord'
- article_kwargs['settings'] = settings
+ article_kwargs["metadata"]["author"] = Author("Alexander Todorov", settings)
+ article_kwargs["metadata"]["title"] = "fnord"
+ article_kwargs["settings"] = settings
article = Article(**article_kwargs)
- self.assertEqual(article.url, 'blog/atodorov/fnord/')
- self.assertEqual(article.save_as, 'blog/atodorov/fnord/index.html')
+ self.assertEqual(article.url, "blog/atodorov/fnord/")
+ self.assertEqual(article.save_as, "blog/atodorov/fnord/index.html")
def test_slugify_category_with_dots(self):
settings = get_settings()
- settings['CATEGORY_REGEX_SUBSTITUTIONS'] = [
- ('Fedora QA', 'fedora.qa'),
+ settings["CATEGORY_REGEX_SUBSTITUTIONS"] = [
+ ("Fedora QA", "fedora.qa"),
]
- settings['ARTICLE_URL'] = '{category}/{slug}/'
+ settings["ARTICLE_URL"] = "{category}/{slug}/"
article_kwargs = self._copy_page_kwargs()
- article_kwargs['metadata']['category'] = Category('Fedora QA',
- settings)
- article_kwargs['metadata']['title'] = 'This Week in Fedora QA'
- article_kwargs['settings'] = settings
+ article_kwargs["metadata"]["category"] = Category("Fedora QA", settings)
+ article_kwargs["metadata"]["title"] = "This Week in Fedora QA"
+ article_kwargs["settings"] = settings
article = Article(**article_kwargs)
- self.assertEqual(article.url, 'fedora.qa/this-week-in-fedora-qa/')
+ self.assertEqual(article.url, "fedora.qa/this-week-in-fedora-qa/")
def test_valid_save_as_detects_breakout(self):
settings = get_settings()
article_kwargs = self._copy_page_kwargs()
- article_kwargs['metadata']['slug'] = '../foo'
- article_kwargs['settings'] = settings
+ article_kwargs["metadata"]["slug"] = "../foo"
+ article_kwargs["settings"] = settings
article = Article(**article_kwargs)
self.assertFalse(article._has_valid_save_as())
def test_valid_save_as_detects_breakout_to_root(self):
settings = get_settings()
article_kwargs = self._copy_page_kwargs()
- article_kwargs['metadata']['slug'] = '/foo'
- article_kwargs['settings'] = settings
+ article_kwargs["metadata"]["slug"] = "/foo"
+ article_kwargs["settings"] = settings
article = Article(**article_kwargs)
self.assertFalse(article._has_valid_save_as())
def test_valid_save_as_passes_valid(self):
settings = get_settings()
article_kwargs = self._copy_page_kwargs()
- article_kwargs['metadata']['slug'] = 'foo'
- article_kwargs['settings'] = settings
+ article_kwargs["metadata"]["slug"] = "foo"
+ article_kwargs["settings"] = settings
article = Article(**article_kwargs)
self.assertTrue(article._has_valid_save_as())
class TestStatic(LoggedTestCase):
-
def setUp(self):
super().setUp()
self.settings = get_settings(
- STATIC_SAVE_AS='{path}',
- STATIC_URL='{path}',
- PAGE_SAVE_AS=os.path.join('outpages', '{slug}.html'),
- PAGE_URL='outpages/{slug}.html')
+ STATIC_SAVE_AS="{path}",
+ STATIC_URL="{path}",
+ PAGE_SAVE_AS=os.path.join("outpages", "{slug}.html"),
+ PAGE_URL="outpages/{slug}.html",
+ )
self.context = get_context(self.settings)
- self.static = Static(content=None, metadata={}, settings=self.settings,
- source_path=posix_join('dir', 'foo.jpg'),
- context=self.context)
+ self.static = Static(
+ content=None,
+ metadata={},
+ settings=self.settings,
+ source_path=posix_join("dir", "foo.jpg"),
+ context=self.context,
+ )
- self.context['static_content'][self.static.source_path] = self.static
+ self.context["static_content"][self.static.source_path] = self.static
def tearDown(self):
pass
def test_attach_to_same_dir(self):
- """attach_to() overrides a static file's save_as and url.
- """
+ """attach_to() overrides a static file's save_as and url."""
page = Page(
content="fake page",
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'fakepage.md'))
+ source_path=os.path.join("dir", "fakepage.md"),
+ )
self.static.attach_to(page)
- expected_save_as = os.path.join('outpages', 'foo.jpg')
+ expected_save_as = os.path.join("outpages", "foo.jpg")
self.assertEqual(self.static.save_as, expected_save_as)
self.assertEqual(self.static.url, path_to_url(expected_save_as))
def test_attach_to_parent_dir(self):
- """attach_to() preserves dirs inside the linking document dir.
- """
- page = Page(content="fake page", metadata={'title': 'fakepage'},
- settings=self.settings, source_path='fakepage.md')
+ """attach_to() preserves dirs inside the linking document dir."""
+ page = Page(
+ content="fake page",
+ metadata={"title": "fakepage"},
+ settings=self.settings,
+ source_path="fakepage.md",
+ )
self.static.attach_to(page)
- expected_save_as = os.path.join('outpages', 'dir', 'foo.jpg')
+ expected_save_as = os.path.join("outpages", "dir", "foo.jpg")
self.assertEqual(self.static.save_as, expected_save_as)
self.assertEqual(self.static.url, path_to_url(expected_save_as))
def test_attach_to_other_dir(self):
- """attach_to() ignores dirs outside the linking document dir.
- """
- page = Page(content="fake page",
- metadata={'title': 'fakepage'}, settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'))
+ """attach_to() ignores dirs outside the linking document dir."""
+ page = Page(
+ content="fake page",
+ metadata={"title": "fakepage"},
+ settings=self.settings,
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ )
self.static.attach_to(page)
- expected_save_as = os.path.join('outpages', 'foo.jpg')
+ expected_save_as = os.path.join("outpages", "foo.jpg")
self.assertEqual(self.static.save_as, expected_save_as)
self.assertEqual(self.static.url, path_to_url(expected_save_as))
def test_attach_to_ignores_subsequent_calls(self):
- """attach_to() does nothing when called a second time.
- """
- page = Page(content="fake page",
- metadata={'title': 'fakepage'}, settings=self.settings,
- source_path=os.path.join('dir', 'fakepage.md'))
+ """attach_to() does nothing when called a second time."""
+ page = Page(
+ content="fake page",
+ metadata={"title": "fakepage"},
+ settings=self.settings,
+ source_path=os.path.join("dir", "fakepage.md"),
+ )
self.static.attach_to(page)
otherdir_settings = self.settings.copy()
- otherdir_settings.update(dict(
- PAGE_SAVE_AS=os.path.join('otherpages', '{slug}.html'),
- PAGE_URL='otherpages/{slug}.html'))
+ otherdir_settings.update(
+ dict(
+ PAGE_SAVE_AS=os.path.join("otherpages", "{slug}.html"),
+ PAGE_URL="otherpages/{slug}.html",
+ )
+ )
otherdir_page = Page(
content="other page",
- metadata={'title': 'otherpage'},
+ metadata={"title": "otherpage"},
settings=otherdir_settings,
- source_path=os.path.join('dir', 'otherpage.md'))
+ source_path=os.path.join("dir", "otherpage.md"),
+ )
self.static.attach_to(otherdir_page)
- otherdir_save_as = os.path.join('otherpages', 'foo.jpg')
+ otherdir_save_as = os.path.join("otherpages", "foo.jpg")
self.assertNotEqual(self.static.save_as, otherdir_save_as)
self.assertNotEqual(self.static.url, path_to_url(otherdir_save_as))
@@ -851,9 +862,10 @@ class TestStatic(LoggedTestCase):
page = Page(
content="fake page",
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'fakepage.md'))
+ source_path=os.path.join("dir", "fakepage.md"),
+ )
self.static.attach_to(page)
self.assertEqual(self.static.save_as, original_save_as)
@@ -867,9 +879,10 @@ class TestStatic(LoggedTestCase):
page = Page(
content="fake page",
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'fakepage.md'))
+ source_path=os.path.join("dir", "fakepage.md"),
+ )
self.static.attach_to(page)
self.assertEqual(self.static.save_as, self.static.source_path)
@@ -881,38 +894,41 @@ class TestStatic(LoggedTestCase):
"""
customstatic = Static(
content=None,
- metadata=dict(save_as='customfoo.jpg', url='customfoo.jpg'),
+ metadata=dict(save_as="customfoo.jpg", url="customfoo.jpg"),
settings=self.settings,
- source_path=os.path.join('dir', 'foo.jpg'),
- context=self.settings.copy())
+ source_path=os.path.join("dir", "foo.jpg"),
+ context=self.settings.copy(),
+ )
page = Page(
content="fake page",
- metadata={'title': 'fakepage'}, settings=self.settings,
- source_path=os.path.join('dir', 'fakepage.md'))
+ metadata={"title": "fakepage"},
+ settings=self.settings,
+ source_path=os.path.join("dir", "fakepage.md"),
+ )
customstatic.attach_to(page)
- self.assertEqual(customstatic.save_as, 'customfoo.jpg')
- self.assertEqual(customstatic.url, 'customfoo.jpg')
+ self.assertEqual(customstatic.save_as, "customfoo.jpg")
+ self.assertEqual(customstatic.url, "customfoo.jpg")
def test_attach_link_syntax(self):
- """{attach} link syntax triggers output path override & url replacement.
- """
+ """{attach} link syntax triggers output path override & url replacement."""
html = 'link'
page = Page(
content=html,
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
- context=self.context)
- content = page.get_content('')
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ context=self.context,
+ )
+ content = page.get_content("")
self.assertNotEqual(
- content, html,
- "{attach} link syntax did not trigger URL replacement.")
+ content, html, "{attach} link syntax did not trigger URL replacement."
+ )
- expected_save_as = os.path.join('outpages', 'foo.jpg')
+ expected_save_as = os.path.join("outpages", "foo.jpg")
self.assertEqual(self.static.save_as, expected_save_as)
self.assertEqual(self.static.url, path_to_url(expected_save_as))
@@ -922,11 +938,12 @@ class TestStatic(LoggedTestCase):
html = 'link'
page = Page(
content=html,
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
- context=self.context)
- content = page.get_content('')
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ context=self.context,
+ )
+ content = page.get_content("")
self.assertNotEqual(content, html)
@@ -936,11 +953,12 @@ class TestStatic(LoggedTestCase):
html = 'link'
page = Page(
content=html,
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
- context=self.context)
- content = page.get_content('')
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ context=self.context,
+ )
+ content = page.get_content("")
self.assertNotEqual(content, html)
@@ -950,11 +968,12 @@ class TestStatic(LoggedTestCase):
html = 'link'
page = Page(
content=html,
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
- context=self.context)
- content = page.get_content('')
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ context=self.context,
+ )
+ content = page.get_content("")
self.assertNotEqual(content, html)
@@ -964,52 +983,62 @@ class TestStatic(LoggedTestCase):
html = 'link'
page = Page(
content=html,
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
- context=self.context)
- content = page.get_content('')
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ context=self.context,
+ )
+ content = page.get_content("")
self.assertNotEqual(content, html)
- expected_html = ('link')
+ expected_html = (
+ 'link'
+ )
self.assertEqual(content, expected_html)
def test_unknown_link_syntax(self):
"{unknown} link syntax should trigger warning."
html = 'link'
- page = Page(content=html,
- metadata={'title': 'fakepage'}, settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
- context=self.context)
- content = page.get_content('')
+ page = Page(
+ content=html,
+ metadata={"title": "fakepage"},
+ settings=self.settings,
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ context=self.context,
+ )
+ content = page.get_content("")
self.assertEqual(content, html)
self.assertLogCountEqual(
count=1,
msg="Replacement Indicator 'unknown' not recognized, "
- "skipping replacement",
- level=logging.WARNING)
+ "skipping replacement",
+ level=logging.WARNING,
+ )
def test_link_to_unknown_file(self):
"{filename} link to unknown file should trigger warning."
html = 'link'
- page = Page(content=html,
- metadata={'title': 'fakepage'}, settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
- context=self.context)
- content = page.get_content('')
+ page = Page(
+ content=html,
+ metadata={"title": "fakepage"},
+ settings=self.settings,
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ context=self.context,
+ )
+ content = page.get_content("")
self.assertEqual(content, html)
self.assertLogCountEqual(
count=1,
msg="Unable to find 'foo', skipping url replacement.",
- level=logging.WARNING)
+ level=logging.WARNING,
+ )
def test_index_link_syntax_with_spaces(self):
"""{index} link syntax triggers url replacement
@@ -1018,18 +1047,20 @@ class TestStatic(LoggedTestCase):
html = 'link'
page = Page(
content=html,
- metadata={'title': 'fakepage'},
+ metadata={"title": "fakepage"},
settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
- context=self.context)
- content = page.get_content('')
+ source_path=os.path.join("dir", "otherdir", "fakepage.md"),
+ context=self.context,
+ )
+ content = page.get_content("")
self.assertNotEqual(content, html)
- expected_html = ('link')
+ expected_html = (
+ 'link'
+ )
self.assertEqual(content, expected_html)
def test_not_save_as_draft(self):
@@ -1037,12 +1068,15 @@ class TestStatic(LoggedTestCase):
static = Static(
content=None,
- metadata=dict(status='draft',),
+ metadata=dict(
+ status="draft",
+ ),
settings=self.settings,
- source_path=os.path.join('dir', 'foo.jpg'),
- context=self.settings.copy())
+ source_path=os.path.join("dir", "foo.jpg"),
+ context=self.settings.copy(),
+ )
- expected_save_as = posixize_path(os.path.join('dir', 'foo.jpg'))
- self.assertEqual(static.status, 'draft')
+ expected_save_as = posixize_path(os.path.join("dir", "foo.jpg"))
+ self.assertEqual(static.status, "draft")
self.assertEqual(static.save_as, expected_save_as)
self.assertEqual(static.url, path_to_url(expected_save_as))
diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py
index 05c37269..52adb2c9 100644
--- a/pelican/tests/test_generators.py
+++ b/pelican/tests/test_generators.py
@@ -4,293 +4,383 @@ from shutil import copy, rmtree
from tempfile import mkdtemp
from unittest.mock import MagicMock
-from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator,
- PelicanTemplateNotFound, StaticGenerator,
- TemplatePagesGenerator)
-from pelican.tests.support import (can_symlink, get_context, get_settings,
- unittest, TestCaseWithCLocale)
+from pelican.generators import (
+ ArticlesGenerator,
+ Generator,
+ PagesGenerator,
+ PelicanTemplateNotFound,
+ StaticGenerator,
+ TemplatePagesGenerator,
+)
+from pelican.tests.support import (
+ can_symlink,
+ get_context,
+ get_settings,
+ unittest,
+ TestCaseWithCLocale,
+)
from pelican.writers import Writer
CUR_DIR = os.path.dirname(__file__)
-CONTENT_DIR = os.path.join(CUR_DIR, 'content')
+CONTENT_DIR = os.path.join(CUR_DIR, "content")
class TestGenerator(TestCaseWithCLocale):
def setUp(self):
super().setUp()
self.settings = get_settings()
- self.settings['READERS'] = {'asc': None}
- self.generator = Generator(self.settings.copy(), self.settings,
- CUR_DIR, self.settings['THEME'], None)
+ self.settings["READERS"] = {"asc": None}
+ self.generator = Generator(
+ self.settings.copy(), self.settings, CUR_DIR, self.settings["THEME"], None
+ )
def test_include_path(self):
- self.settings['IGNORE_FILES'] = {'ignored1.rst', 'ignored2.rst'}
+ self.settings["IGNORE_FILES"] = {"ignored1.rst", "ignored2.rst"}
- filename = os.path.join(CUR_DIR, 'content', 'article.rst')
+ filename = os.path.join(CUR_DIR, "content", "article.rst")
include_path = self.generator._include_path
self.assertTrue(include_path(filename))
- self.assertTrue(include_path(filename, extensions=('rst',)))
- self.assertFalse(include_path(filename, extensions=('md',)))
+ self.assertTrue(include_path(filename, extensions=("rst",)))
+ self.assertFalse(include_path(filename, extensions=("md",)))
- ignored_file = os.path.join(CUR_DIR, 'content', 'ignored1.rst')
+ ignored_file = os.path.join(CUR_DIR, "content", "ignored1.rst")
self.assertFalse(include_path(ignored_file))
def test_get_files_exclude(self):
- """Test that Generator.get_files() properly excludes directories.
- """
+ """Test that Generator.get_files() properly excludes directories."""
# We use our own Generator so we can give it our own content path
generator = Generator(
context=self.settings.copy(),
settings=self.settings,
- path=os.path.join(CUR_DIR, 'nested_content'),
- theme=self.settings['THEME'], output_path=None)
+ path=os.path.join(CUR_DIR, "nested_content"),
+ theme=self.settings["THEME"],
+ output_path=None,
+ )
- filepaths = generator.get_files(paths=['maindir'])
+ filepaths = generator.get_files(paths=["maindir"])
found_files = {os.path.basename(f) for f in filepaths}
- expected_files = {'maindir.md', 'subdir.md'}
+ expected_files = {"maindir.md", "subdir.md"}
self.assertFalse(
- expected_files - found_files,
- "get_files() failed to find one or more files")
+ expected_files - found_files, "get_files() failed to find one or more files"
+ )
# Test string as `paths` argument rather than list
- filepaths = generator.get_files(paths='maindir')
+ filepaths = generator.get_files(paths="maindir")
found_files = {os.path.basename(f) for f in filepaths}
- expected_files = {'maindir.md', 'subdir.md'}
+ expected_files = {"maindir.md", "subdir.md"}
self.assertFalse(
- expected_files - found_files,
- "get_files() failed to find one or more files")
+ expected_files - found_files, "get_files() failed to find one or more files"
+ )
- filepaths = generator.get_files(paths=[''], exclude=['maindir'])
+ filepaths = generator.get_files(paths=[""], exclude=["maindir"])
found_files = {os.path.basename(f) for f in filepaths}
self.assertNotIn(
- 'maindir.md', found_files,
- "get_files() failed to exclude a top-level directory")
+ "maindir.md",
+ found_files,
+ "get_files() failed to exclude a top-level directory",
+ )
self.assertNotIn(
- 'subdir.md', found_files,
- "get_files() failed to exclude a subdir of an excluded directory")
+ "subdir.md",
+ found_files,
+ "get_files() failed to exclude a subdir of an excluded directory",
+ )
filepaths = generator.get_files(
- paths=[''],
- exclude=[os.path.join('maindir', 'subdir')])
+ paths=[""], exclude=[os.path.join("maindir", "subdir")]
+ )
found_files = {os.path.basename(f) for f in filepaths}
self.assertNotIn(
- 'subdir.md', found_files,
- "get_files() failed to exclude a subdirectory")
+ "subdir.md", found_files, "get_files() failed to exclude a subdirectory"
+ )
- filepaths = generator.get_files(paths=[''], exclude=['subdir'])
+ filepaths = generator.get_files(paths=[""], exclude=["subdir"])
found_files = {os.path.basename(f) for f in filepaths}
self.assertIn(
- 'subdir.md', found_files,
- "get_files() excluded a subdirectory by name, ignoring its path")
+ "subdir.md",
+ found_files,
+ "get_files() excluded a subdirectory by name, ignoring its path",
+ )
def test_custom_jinja_environment(self):
"""
- Test that setting the JINJA_ENVIRONMENT
- properly gets set from the settings config
+ Test that setting the JINJA_ENVIRONMENT
+ properly gets set from the settings config
"""
settings = get_settings()
- comment_start_string = 'abc'
- comment_end_string = '/abc'
- settings['JINJA_ENVIRONMENT'] = {
- 'comment_start_string': comment_start_string,
- 'comment_end_string': comment_end_string
+ comment_start_string = "abc"
+ comment_end_string = "/abc"
+ settings["JINJA_ENVIRONMENT"] = {
+ "comment_start_string": comment_start_string,
+ "comment_end_string": comment_end_string,
}
- generator = Generator(settings.copy(), settings,
- CUR_DIR, settings['THEME'], None)
- self.assertEqual(comment_start_string,
- generator.env.comment_start_string)
- self.assertEqual(comment_end_string,
- generator.env.comment_end_string)
+ generator = Generator(
+ settings.copy(), settings, CUR_DIR, settings["THEME"], None
+ )
+ self.assertEqual(comment_start_string, generator.env.comment_start_string)
+ self.assertEqual(comment_end_string, generator.env.comment_end_string)
def test_theme_overrides(self):
"""
- Test that the THEME_TEMPLATES_OVERRIDES configuration setting is
- utilized correctly in the Generator.
+ Test that the THEME_TEMPLATES_OVERRIDES configuration setting is
+ utilized correctly in the Generator.
"""
- override_dirs = (os.path.join(CUR_DIR, 'theme_overrides', 'level1'),
- os.path.join(CUR_DIR, 'theme_overrides', 'level2'))
- self.settings['THEME_TEMPLATES_OVERRIDES'] = override_dirs
+ override_dirs = (
+ os.path.join(CUR_DIR, "theme_overrides", "level1"),
+ os.path.join(CUR_DIR, "theme_overrides", "level2"),
+ )
+ self.settings["THEME_TEMPLATES_OVERRIDES"] = override_dirs
generator = Generator(
context=self.settings.copy(),
settings=self.settings,
path=CUR_DIR,
- theme=self.settings['THEME'],
- output_path=None)
+ theme=self.settings["THEME"],
+ output_path=None,
+ )
- filename = generator.get_template('article').filename
+ filename = generator.get_template("article").filename
self.assertEqual(override_dirs[0], os.path.dirname(filename))
- self.assertEqual('article.html', os.path.basename(filename))
+ self.assertEqual("article.html", os.path.basename(filename))
- filename = generator.get_template('authors').filename
+ filename = generator.get_template("authors").filename
self.assertEqual(override_dirs[1], os.path.dirname(filename))
- self.assertEqual('authors.html', os.path.basename(filename))
+ self.assertEqual("authors.html", os.path.basename(filename))
- filename = generator.get_template('taglist').filename
- self.assertEqual(os.path.join(self.settings['THEME'], 'templates'),
- os.path.dirname(filename))
+ filename = generator.get_template("taglist").filename
+ self.assertEqual(
+ os.path.join(self.settings["THEME"], "templates"), os.path.dirname(filename)
+ )
self.assertNotIn(os.path.dirname(filename), override_dirs)
- self.assertEqual('taglist.html', os.path.basename(filename))
+ self.assertEqual("taglist.html", os.path.basename(filename))
def test_simple_prefix(self):
"""
- Test `!simple` theme prefix.
+ Test `!simple` theme prefix.
"""
- filename = self.generator.get_template('!simple/authors').filename
+ filename = self.generator.get_template("!simple/authors").filename
expected_path = os.path.join(
- os.path.dirname(CUR_DIR), 'themes', 'simple', 'templates')
+ os.path.dirname(CUR_DIR), "themes", "simple", "templates"
+ )
self.assertEqual(expected_path, os.path.dirname(filename))
- self.assertEqual('authors.html', os.path.basename(filename))
+ self.assertEqual("authors.html", os.path.basename(filename))
def test_theme_prefix(self):
"""
- Test `!theme` theme prefix.
+ Test `!theme` theme prefix.
"""
- filename = self.generator.get_template('!theme/authors').filename
+ filename = self.generator.get_template("!theme/authors").filename
expected_path = os.path.join(
- os.path.dirname(CUR_DIR), 'themes', 'notmyidea', 'templates')
+ os.path.dirname(CUR_DIR), "themes", "notmyidea", "templates"
+ )
self.assertEqual(expected_path, os.path.dirname(filename))
- self.assertEqual('authors.html', os.path.basename(filename))
+ self.assertEqual("authors.html", os.path.basename(filename))
def test_bad_prefix(self):
"""
- Test unknown/bad theme prefix throws exception.
+ Test unknown/bad theme prefix throws exception.
"""
- self.assertRaises(PelicanTemplateNotFound, self.generator.get_template,
- '!UNKNOWN/authors')
+ self.assertRaises(
+ PelicanTemplateNotFound, self.generator.get_template, "!UNKNOWN/authors"
+ )
class TestArticlesGenerator(unittest.TestCase):
-
@classmethod
def setUpClass(cls):
settings = get_settings()
- settings['DEFAULT_CATEGORY'] = 'Default'
- settings['DEFAULT_DATE'] = (1970, 1, 1)
- settings['READERS'] = {'asc': None}
- settings['CACHE_CONTENT'] = False
+ settings["DEFAULT_CATEGORY"] = "Default"
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
+ settings["READERS"] = {"asc": None}
+ settings["CACHE_CONTENT"] = False
context = get_context(settings)
cls.generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
cls.generator.generate_context()
cls.articles = cls.distill_articles(cls.generator.articles)
cls.drafts = cls.distill_articles(cls.generator.drafts)
cls.hidden_articles = cls.distill_articles(cls.generator.hidden_articles)
def setUp(self):
- self.temp_cache = mkdtemp(prefix='pelican_cache.')
+ self.temp_cache = mkdtemp(prefix="pelican_cache.")
def tearDown(self):
rmtree(self.temp_cache)
@staticmethod
def distill_articles(articles):
- return [[article.title, article.status, article.category.name,
- article.template] for article in articles]
+ return [
+ [article.title, article.status, article.category.name, article.template]
+ for article in articles
+ ]
def test_generate_feeds(self):
settings = get_settings()
- settings['CACHE_PATH'] = self.temp_cache
+ settings["CACHE_PATH"] = self.temp_cache
generator = ArticlesGenerator(
- context=settings, settings=settings,
- path=None, theme=settings['THEME'], output_path=None)
+ context=settings,
+ settings=settings,
+ path=None,
+ theme=settings["THEME"],
+ output_path=None,
+ )
writer = MagicMock()
generator.generate_feeds(writer)
- writer.write_feed.assert_called_with([], settings,
- 'feeds/all.atom.xml',
- 'feeds/all.atom.xml')
+ writer.write_feed.assert_called_with(
+ [], settings, "feeds/all.atom.xml", "feeds/all.atom.xml"
+ )
generator = ArticlesGenerator(
- context=settings, settings=get_settings(FEED_ALL_ATOM=None),
- path=None, theme=settings['THEME'], output_path=None)
+ context=settings,
+ settings=get_settings(FEED_ALL_ATOM=None),
+ path=None,
+ theme=settings["THEME"],
+ output_path=None,
+ )
writer = MagicMock()
generator.generate_feeds(writer)
self.assertFalse(writer.write_feed.called)
def test_generate_feeds_override_url(self):
settings = get_settings()
- settings['CACHE_PATH'] = self.temp_cache
- settings['FEED_ALL_ATOM_URL'] = 'feeds/atom/all/'
+ settings["CACHE_PATH"] = self.temp_cache
+ settings["FEED_ALL_ATOM_URL"] = "feeds/atom/all/"
generator = ArticlesGenerator(
- context=settings, settings=settings,
- path=None, theme=settings['THEME'], output_path=None)
+ context=settings,
+ settings=settings,
+ path=None,
+ theme=settings["THEME"],
+ output_path=None,
+ )
writer = MagicMock()
generator.generate_feeds(writer)
- writer.write_feed.assert_called_with([], settings,
- 'feeds/all.atom.xml',
- 'feeds/atom/all/')
+ writer.write_feed.assert_called_with(
+ [], settings, "feeds/all.atom.xml", "feeds/atom/all/"
+ )
def test_generate_context(self):
articles_expected = [
- ['Article title', 'published', 'Default', 'article'],
- ['Article with markdown and summary metadata multi', 'published',
- 'Default', 'article'],
- ['Article with markdown and nested summary metadata', 'published',
- 'Default', 'article'],
- ['Article with markdown and summary metadata single', 'published',
- 'Default', 'article'],
- ['Article with markdown containing footnotes', 'published',
- 'Default', 'article'],
- ['Article with template', 'published', 'Default', 'custom'],
- ['Metadata tags as list!', 'published', 'Default', 'article'],
- ['Rst with filename metadata', 'published', 'yeah', 'article'],
- ['One -, two --, three --- dashes!', 'published', 'Default',
- 'article'],
- ['One -, two --, three --- dashes!', 'published', 'Default',
- 'article'],
- ['Test Markdown extensions', 'published', 'Default', 'article'],
- ['Test markdown File', 'published', 'test', 'article'],
- ['Test md File', 'published', 'test', 'article'],
- ['Test mdown File', 'published', 'test', 'article'],
- ['Test metadata duplicates', 'published', 'test', 'article'],
- ['Test mkd File', 'published', 'test', 'article'],
- ['This is a super article !', 'published', 'Yeah', 'article'],
- ['This is a super article !', 'published', 'Yeah', 'article'],
- ['Article with Nonconformant HTML meta tags', 'published',
- 'Default', 'article'],
- ['This is a super article !', 'published', 'yeah', 'article'],
- ['This is a super article !', 'published', 'yeah', 'article'],
- ['This is a super article !', 'published', 'yeah', 'article'],
- ['This is a super article !', 'published', 'yeah', 'article'],
- ['This is a super article !', 'published', 'yeah', 'article'],
- ['This is a super article !', 'published', 'yeah', 'article'],
- ['This is a super article !', 'published', 'yeah', 'article'],
- ['This is a super article !', 'published', 'yeah', 'article'],
- ['This is a super article !', 'published', 'Default', 'article'],
- ['Article with an inline SVG', 'published', 'Default', 'article'],
- ['Article with markdown and empty tags', 'published', 'Default',
- 'article'],
- ['This is an article with category !', 'published', 'yeah',
- 'article'],
- ['This is an article with multiple authors!', 'published',
- 'Default', 'article'],
- ['This is an article with multiple authors!', 'published',
- 'Default', 'article'],
- ['This is an article with multiple authors in list format!',
- 'published', 'Default', 'article'],
- ['This is an article with multiple authors in lastname, '
- 'firstname format!', 'published', 'Default', 'article'],
- ['This is an article without category !', 'published', 'Default',
- 'article'],
- ['This is an article without category !', 'published',
- 'TestCategory', 'article'],
- ['An Article With Code Block To Test Typogrify Ignore',
- 'published', 'Default', 'article'],
- ['マックOS X 10.8でパイソンとVirtualenvをインストールと設定',
- 'published', '指導書', 'article'],
+ ["Article title", "published", "Default", "article"],
+ [
+ "Article with markdown and summary metadata multi",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "Article with markdown and nested summary metadata",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "Article with markdown and summary metadata single",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "Article with markdown containing footnotes",
+ "published",
+ "Default",
+ "article",
+ ],
+ ["Article with template", "published", "Default", "custom"],
+ ["Metadata tags as list!", "published", "Default", "article"],
+ ["Rst with filename metadata", "published", "yeah", "article"],
+ ["One -, two --, three --- dashes!", "published", "Default", "article"],
+ ["One -, two --, three --- dashes!", "published", "Default", "article"],
+ ["Test Markdown extensions", "published", "Default", "article"],
+ ["Test markdown File", "published", "test", "article"],
+ ["Test md File", "published", "test", "article"],
+ ["Test mdown File", "published", "test", "article"],
+ ["Test metadata duplicates", "published", "test", "article"],
+ ["Test mkd File", "published", "test", "article"],
+ ["This is a super article !", "published", "Yeah", "article"],
+ ["This is a super article !", "published", "Yeah", "article"],
+ [
+ "Article with Nonconformant HTML meta tags",
+ "published",
+ "Default",
+ "article",
+ ],
+ ["This is a super article !", "published", "yeah", "article"],
+ ["This is a super article !", "published", "yeah", "article"],
+ ["This is a super article !", "published", "yeah", "article"],
+ ["This is a super article !", "published", "yeah", "article"],
+ ["This is a super article !", "published", "yeah", "article"],
+ ["This is a super article !", "published", "yeah", "article"],
+ ["This is a super article !", "published", "yeah", "article"],
+ ["This is a super article !", "published", "yeah", "article"],
+ ["This is a super article !", "published", "Default", "article"],
+ ["Article with an inline SVG", "published", "Default", "article"],
+ ["Article with markdown and empty tags", "published", "Default", "article"],
+ ["This is an article with category !", "published", "yeah", "article"],
+ [
+ "This is an article with multiple authors!",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "This is an article with multiple authors!",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "This is an article with multiple authors in list format!",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "This is an article with multiple authors in lastname, "
+ "firstname format!",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "This is an article without category !",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "This is an article without category !",
+ "published",
+ "TestCategory",
+ "article",
+ ],
+ [
+ "An Article With Code Block To Test Typogrify Ignore",
+ "published",
+ "Default",
+ "article",
+ ],
+ [
+ "マックOS X 10.8でパイソンとVirtualenvをインストールと設定",
+ "published",
+ "指導書",
+ "article",
+ ],
]
self.assertEqual(sorted(articles_expected), sorted(self.articles))
def test_articles_draft(self):
draft_articles_expected = [
- ['Draft article', 'draft', 'Default', 'article'],
+ ["Draft article", "draft", "Default", "article"],
]
self.assertEqual(sorted(draft_articles_expected), sorted(self.drafts))
def test_articles_hidden(self):
hidden_articles_expected = [
- ['Hidden article', 'hidden', 'Default', 'article'],
+ ["Hidden article", "hidden", "Default", "article"],
]
self.assertEqual(sorted(hidden_articles_expected), sorted(self.hidden_articles))
@@ -301,27 +391,30 @@ class TestArticlesGenerator(unittest.TestCase):
# terms of process order will define the name for that category
categories = [cat.name for cat, _ in self.generator.categories]
categories_alternatives = (
- sorted(['Default', 'TestCategory', 'Yeah', 'test', '指導書']),
- sorted(['Default', 'TestCategory', 'yeah', 'test', '指導書']),
+ sorted(["Default", "TestCategory", "Yeah", "test", "指導書"]),
+ sorted(["Default", "TestCategory", "yeah", "test", "指導書"]),
)
self.assertIn(sorted(categories), categories_alternatives)
# test for slug
categories = [cat.slug for cat, _ in self.generator.categories]
- categories_expected = ['default', 'testcategory', 'yeah', 'test',
- 'zhi-dao-shu']
+ categories_expected = ["default", "testcategory", "yeah", "test", "zhi-dao-shu"]
self.assertEqual(sorted(categories), sorted(categories_expected))
def test_do_not_use_folder_as_category(self):
settings = get_settings()
- settings['DEFAULT_CATEGORY'] = 'Default'
- settings['DEFAULT_DATE'] = (1970, 1, 1)
- settings['USE_FOLDER_AS_CATEGORY'] = False
- settings['CACHE_PATH'] = self.temp_cache
- settings['READERS'] = {'asc': None}
+ settings["DEFAULT_CATEGORY"] = "Default"
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
+ settings["USE_FOLDER_AS_CATEGORY"] = False
+ settings["CACHE_PATH"] = self.temp_cache
+ settings["READERS"] = {"asc": None}
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
# test for name
# categories are grouped by slug; if two categories have the same slug
@@ -329,61 +422,79 @@ class TestArticlesGenerator(unittest.TestCase):
# terms of process order will define the name for that category
categories = [cat.name for cat, _ in generator.categories]
categories_alternatives = (
- sorted(['Default', 'Yeah', 'test', '指導書']),
- sorted(['Default', 'yeah', 'test', '指導書']),
+ sorted(["Default", "Yeah", "test", "指導書"]),
+ sorted(["Default", "yeah", "test", "指導書"]),
)
self.assertIn(sorted(categories), categories_alternatives)
# test for slug
categories = [cat.slug for cat, _ in generator.categories]
- categories_expected = ['default', 'yeah', 'test', 'zhi-dao-shu']
+ categories_expected = ["default", "yeah", "test", "zhi-dao-shu"]
self.assertEqual(sorted(categories), sorted(categories_expected))
def test_direct_templates_save_as_url_default(self):
-
settings = get_settings()
- settings['CACHE_PATH'] = self.temp_cache
+ settings["CACHE_PATH"] = self.temp_cache
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=None, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=None,
+ theme=settings["THEME"],
+ output_path=None,
+ )
write = MagicMock()
generator.generate_direct_templates(write)
- write.assert_called_with("archives.html",
- generator.get_template("archives"), context,
- articles=generator.articles,
- dates=generator.dates, blog=True,
- template_name='archives',
- page_name='archives', url="archives.html")
+ write.assert_called_with(
+ "archives.html",
+ generator.get_template("archives"),
+ context,
+ articles=generator.articles,
+ dates=generator.dates,
+ blog=True,
+ template_name="archives",
+ page_name="archives",
+ url="archives.html",
+ )
def test_direct_templates_save_as_url_modified(self):
-
settings = get_settings()
- settings['DIRECT_TEMPLATES'] = ['archives']
- settings['ARCHIVES_SAVE_AS'] = 'archives/index.html'
- settings['ARCHIVES_URL'] = 'archives/'
- settings['CACHE_PATH'] = self.temp_cache
+ settings["DIRECT_TEMPLATES"] = ["archives"]
+ settings["ARCHIVES_SAVE_AS"] = "archives/index.html"
+ settings["ARCHIVES_URL"] = "archives/"
+ settings["CACHE_PATH"] = self.temp_cache
generator = ArticlesGenerator(
- context=settings, settings=settings,
- path=None, theme=settings['THEME'], output_path=None)
+ context=settings,
+ settings=settings,
+ path=None,
+ theme=settings["THEME"],
+ output_path=None,
+ )
write = MagicMock()
generator.generate_direct_templates(write)
- write.assert_called_with("archives/index.html",
- generator.get_template("archives"), settings,
- articles=generator.articles,
- dates=generator.dates, blog=True,
- template_name='archives',
- page_name='archives/index',
- url="archives/")
+ write.assert_called_with(
+ "archives/index.html",
+ generator.get_template("archives"),
+ settings,
+ articles=generator.articles,
+ dates=generator.dates,
+ blog=True,
+ template_name="archives",
+ page_name="archives/index",
+ url="archives/",
+ )
def test_direct_templates_save_as_false(self):
-
settings = get_settings()
- settings['DIRECT_TEMPLATES'] = ['archives']
- settings['ARCHIVES_SAVE_AS'] = False
- settings['CACHE_PATH'] = self.temp_cache
+ settings["DIRECT_TEMPLATES"] = ["archives"]
+ settings["ARCHIVES_SAVE_AS"] = False
+ settings["CACHE_PATH"] = self.temp_cache
generator = ArticlesGenerator(
- context=settings, settings=settings,
- path=None, theme=settings['THEME'], output_path=None)
+ context=settings,
+ settings=settings,
+ path=None,
+ theme=settings["THEME"],
+ output_path=None,
+ )
write = MagicMock()
generator.generate_direct_templates(write)
self.assertEqual(write.call_count, 0)
@@ -392,10 +503,13 @@ class TestArticlesGenerator(unittest.TestCase):
"""
Custom template articles get the field but standard/unset are None
"""
- custom_template = ['Article with template', 'published', 'Default',
- 'custom']
- standard_template = ['This is a super article !', 'published', 'Yeah',
- 'article']
+ custom_template = ["Article with template", "published", "Default", "custom"]
+ standard_template = [
+ "This is a super article !",
+ "published",
+ "Yeah",
+ "article",
+ ]
self.assertIn(custom_template, self.articles)
self.assertIn(standard_template, self.articles)
@@ -403,126 +517,135 @@ class TestArticlesGenerator(unittest.TestCase):
"""Test correctness of the period_archives context values."""
settings = get_settings()
- settings['CACHE_PATH'] = self.temp_cache
+ settings["CACHE_PATH"] = self.temp_cache
# No period archives enabled:
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
- period_archives = generator.context['period_archives']
+ period_archives = generator.context["period_archives"]
self.assertEqual(len(period_archives.items()), 0)
# Year archives enabled:
- settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html'
- settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/'
+ settings["YEAR_ARCHIVE_SAVE_AS"] = "posts/{date:%Y}/index.html"
+ settings["YEAR_ARCHIVE_URL"] = "posts/{date:%Y}/"
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
- period_archives = generator.context['period_archives']
+ period_archives = generator.context["period_archives"]
abbreviated_archives = {
- granularity: {period['period'] for period in periods}
+ granularity: {period["period"] for period in periods}
for granularity, periods in period_archives.items()
}
- expected = {'year': {(1970,), (2010,), (2012,), (2014,)}}
+ expected = {"year": {(1970,), (2010,), (2012,), (2014,)}}
self.assertEqual(expected, abbreviated_archives)
# Month archives enabled:
- settings['MONTH_ARCHIVE_SAVE_AS'] = \
- 'posts/{date:%Y}/{date:%b}/index.html'
- settings['MONTH_ARCHIVE_URL'] = \
- 'posts/{date:%Y}/{date:%b}/'
+ settings["MONTH_ARCHIVE_SAVE_AS"] = "posts/{date:%Y}/{date:%b}/index.html"
+ settings["MONTH_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/"
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
- period_archives = generator.context['period_archives']
+ period_archives = generator.context["period_archives"]
abbreviated_archives = {
- granularity: {period['period'] for period in periods}
+ granularity: {period["period"] for period in periods}
for granularity, periods in period_archives.items()
}
expected = {
- 'year': {(1970,), (2010,), (2012,), (2014,)},
- 'month': {
- (1970, 'January'),
- (2010, 'December'),
- (2012, 'December'),
- (2012, 'November'),
- (2012, 'October'),
- (2014, 'February'),
+ "year": {(1970,), (2010,), (2012,), (2014,)},
+ "month": {
+ (1970, "January"),
+ (2010, "December"),
+ (2012, "December"),
+ (2012, "November"),
+ (2012, "October"),
+ (2014, "February"),
},
}
self.assertEqual(expected, abbreviated_archives)
# Day archives enabled:
- settings['DAY_ARCHIVE_SAVE_AS'] = \
- 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
- settings['DAY_ARCHIVE_URL'] = \
- 'posts/{date:%Y}/{date:%b}/{date:%d}/'
+ settings[
+ "DAY_ARCHIVE_SAVE_AS"
+ ] = "posts/{date:%Y}/{date:%b}/{date:%d}/index.html"
+ settings["DAY_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/{date:%d}/"
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
- period_archives = generator.context['period_archives']
+ period_archives = generator.context["period_archives"]
abbreviated_archives = {
- granularity: {period['period'] for period in periods}
+ granularity: {period["period"] for period in periods}
for granularity, periods in period_archives.items()
}
expected = {
- 'year': {(1970,), (2010,), (2012,), (2014,)},
- 'month': {
- (1970, 'January'),
- (2010, 'December'),
- (2012, 'December'),
- (2012, 'November'),
- (2012, 'October'),
- (2014, 'February'),
+ "year": {(1970,), (2010,), (2012,), (2014,)},
+ "month": {
+ (1970, "January"),
+ (2010, "December"),
+ (2012, "December"),
+ (2012, "November"),
+ (2012, "October"),
+ (2014, "February"),
},
- 'day': {
- (1970, 'January', 1),
- (2010, 'December', 2),
- (2012, 'December', 20),
- (2012, 'November', 29),
- (2012, 'October', 30),
- (2012, 'October', 31),
- (2014, 'February', 9),
+ "day": {
+ (1970, "January", 1),
+ (2010, "December", 2),
+ (2012, "December", 20),
+ (2012, "November", 29),
+ (2012, "October", 30),
+ (2012, "October", 31),
+ (2014, "February", 9),
},
}
self.assertEqual(expected, abbreviated_archives)
# Further item values tests
filtered_archives = [
- p for p in period_archives['day']
- if p['period'] == (2014, 'February', 9)
+ p for p in period_archives["day"] if p["period"] == (2014, "February", 9)
]
self.assertEqual(len(filtered_archives), 1)
sample_archive = filtered_archives[0]
- self.assertEqual(sample_archive['period_num'], (2014, 2, 9))
- self.assertEqual(
- sample_archive['save_as'], 'posts/2014/Feb/09/index.html')
- self.assertEqual(
- sample_archive['url'], 'posts/2014/Feb/09/')
+ self.assertEqual(sample_archive["period_num"], (2014, 2, 9))
+ self.assertEqual(sample_archive["save_as"], "posts/2014/Feb/09/index.html")
+ self.assertEqual(sample_archive["url"], "posts/2014/Feb/09/")
articles = [
- d for d in generator.articles if
- d.date.year == 2014 and
- d.date.month == 2 and
- d.date.day == 9
+ d
+ for d in generator.articles
+ if d.date.year == 2014 and d.date.month == 2 and d.date.day == 9
]
- self.assertEqual(len(sample_archive['articles']), len(articles))
+ self.assertEqual(len(sample_archive["articles"]), len(articles))
dates = [
- d for d in generator.dates if
- d.date.year == 2014 and
- d.date.month == 2 and
- d.date.day == 9
+ d
+ for d in generator.dates
+ if d.date.year == 2014 and d.date.month == 2 and d.date.day == 9
]
- self.assertEqual(len(sample_archive['dates']), len(dates))
- self.assertEqual(sample_archive['dates'][0].title, dates[0].title)
- self.assertEqual(sample_archive['dates'][0].date, dates[0].date)
+ self.assertEqual(len(sample_archive["dates"]), len(dates))
+ self.assertEqual(sample_archive["dates"][0].title, dates[0].title)
+ self.assertEqual(sample_archive["dates"][0].date, dates[0].date)
def test_period_in_timeperiod_archive(self):
"""
@@ -531,13 +654,17 @@ class TestArticlesGenerator(unittest.TestCase):
"""
settings = get_settings()
- settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html'
- settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/'
- settings['CACHE_PATH'] = self.temp_cache
+ settings["YEAR_ARCHIVE_SAVE_AS"] = "posts/{date:%Y}/index.html"
+ settings["YEAR_ARCHIVE_URL"] = "posts/{date:%Y}/"
+ settings["CACHE_PATH"] = self.temp_cache
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
write = MagicMock()
generator.generate_period_archives(write)
@@ -547,196 +674,257 @@ class TestArticlesGenerator(unittest.TestCase):
# among other things it must have at least been called with this
context["period"] = (1970,)
context["period_num"] = (1970,)
- write.assert_called_with("posts/1970/index.html",
- generator.get_template("period_archives"),
- context, blog=True, articles=articles,
- dates=dates, template_name='period_archives',
- url="posts/1970/",
- all_articles=generator.articles)
+ write.assert_called_with(
+ "posts/1970/index.html",
+ generator.get_template("period_archives"),
+ context,
+ blog=True,
+ articles=articles,
+ dates=dates,
+ template_name="period_archives",
+ url="posts/1970/",
+ all_articles=generator.articles,
+ )
- settings['MONTH_ARCHIVE_SAVE_AS'] = \
- 'posts/{date:%Y}/{date:%b}/index.html'
- settings['MONTH_ARCHIVE_URL'] = \
- 'posts/{date:%Y}/{date:%b}/'
+ settings["MONTH_ARCHIVE_SAVE_AS"] = "posts/{date:%Y}/{date:%b}/index.html"
+ settings["MONTH_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/"
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
- generator.generate_context()
- write = MagicMock()
- generator.generate_period_archives(write)
- dates = [d for d in generator.dates
- if d.date.year == 1970 and d.date.month == 1]
- articles = [d for d in generator.articles
- if d.date.year == 1970 and d.date.month == 1]
- self.assertEqual(len(dates), 1)
- context["period"] = (1970, "January")
- context["period_num"] = (1970, 1)
- # among other things it must have at least been called with this
- write.assert_called_with("posts/1970/Jan/index.html",
- generator.get_template("period_archives"),
- context, blog=True, articles=articles,
- dates=dates, template_name='period_archives',
- url="posts/1970/Jan/",
- all_articles=generator.articles)
-
- settings['DAY_ARCHIVE_SAVE_AS'] = \
- 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
- settings['DAY_ARCHIVE_URL'] = \
- 'posts/{date:%Y}/{date:%b}/{date:%d}/'
- context = get_context(settings)
- generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
write = MagicMock()
generator.generate_period_archives(write)
dates = [
- d for d in generator.dates if
- d.date.year == 1970 and
- d.date.month == 1 and
- d.date.day == 1
+ d for d in generator.dates if d.date.year == 1970 and d.date.month == 1
]
articles = [
- d for d in generator.articles if
- d.date.year == 1970 and
- d.date.month == 1 and
- d.date.day == 1
+ d for d in generator.articles if d.date.year == 1970 and d.date.month == 1
+ ]
+ self.assertEqual(len(dates), 1)
+ context["period"] = (1970, "January")
+ context["period_num"] = (1970, 1)
+ # among other things it must have at least been called with this
+ write.assert_called_with(
+ "posts/1970/Jan/index.html",
+ generator.get_template("period_archives"),
+ context,
+ blog=True,
+ articles=articles,
+ dates=dates,
+ template_name="period_archives",
+ url="posts/1970/Jan/",
+ all_articles=generator.articles,
+ )
+
+ settings[
+ "DAY_ARCHIVE_SAVE_AS"
+ ] = "posts/{date:%Y}/{date:%b}/{date:%d}/index.html"
+ settings["DAY_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/{date:%d}/"
+ context = get_context(settings)
+ generator = ArticlesGenerator(
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
+ generator.generate_context()
+ write = MagicMock()
+ generator.generate_period_archives(write)
+ dates = [
+ d
+ for d in generator.dates
+ if d.date.year == 1970 and d.date.month == 1 and d.date.day == 1
+ ]
+ articles = [
+ d
+ for d in generator.articles
+ if d.date.year == 1970 and d.date.month == 1 and d.date.day == 1
]
self.assertEqual(len(dates), 1)
context["period"] = (1970, "January", 1)
context["period_num"] = (1970, 1, 1)
# among other things it must have at least been called with this
- write.assert_called_with("posts/1970/Jan/01/index.html",
- generator.get_template("period_archives"),
- context, blog=True, articles=articles,
- dates=dates, template_name='period_archives',
- url="posts/1970/Jan/01/",
- all_articles=generator.articles)
+ write.assert_called_with(
+ "posts/1970/Jan/01/index.html",
+ generator.get_template("period_archives"),
+ context,
+ blog=True,
+ articles=articles,
+ dates=dates,
+ template_name="period_archives",
+ url="posts/1970/Jan/01/",
+ all_articles=generator.articles,
+ )
def test_nonexistent_template(self):
"""Attempt to load a non-existent template"""
settings = get_settings()
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=None, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=None,
+ theme=settings["THEME"],
+ output_path=None,
+ )
self.assertRaises(Exception, generator.get_template, "not_a_template")
def test_generate_authors(self):
"""Check authors generation."""
authors = [author.name for author, _ in self.generator.authors]
authors_expected = sorted(
- ['Alexis Métaireau', 'Author, First', 'Author, Second',
- 'First Author', 'Second Author'])
+ [
+ "Alexis Métaireau",
+ "Author, First",
+ "Author, Second",
+ "First Author",
+ "Second Author",
+ ]
+ )
self.assertEqual(sorted(authors), authors_expected)
# test for slug
authors = [author.slug for author, _ in self.generator.authors]
- authors_expected = ['alexis-metaireau', 'author-first',
- 'author-second', 'first-author', 'second-author']
+ authors_expected = [
+ "alexis-metaireau",
+ "author-first",
+ "author-second",
+ "first-author",
+ "second-author",
+ ]
self.assertEqual(sorted(authors), sorted(authors_expected))
def test_standard_metadata_in_default_metadata(self):
settings = get_settings()
- settings['CACHE_CONTENT'] = False
- settings['DEFAULT_CATEGORY'] = 'Default'
- settings['DEFAULT_DATE'] = (1970, 1, 1)
- settings['DEFAULT_METADATA'] = (('author', 'Blogger'),
- # category will be ignored in favor of
- # DEFAULT_CATEGORY
- ('category', 'Random'),
- ('tags', 'general, untagged'))
+ settings["CACHE_CONTENT"] = False
+ settings["DEFAULT_CATEGORY"] = "Default"
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
+ settings["DEFAULT_METADATA"] = (
+ ("author", "Blogger"),
+ # category will be ignored in favor of
+ # DEFAULT_CATEGORY
+ ("category", "Random"),
+ ("tags", "general, untagged"),
+ )
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
authors = sorted([author.name for author, _ in generator.authors])
- authors_expected = sorted(['Alexis Métaireau', 'Blogger',
- 'Author, First', 'Author, Second',
- 'First Author', 'Second Author'])
+ authors_expected = sorted(
+ [
+ "Alexis Métaireau",
+ "Blogger",
+ "Author, First",
+ "Author, Second",
+ "First Author",
+ "Second Author",
+ ]
+ )
self.assertEqual(authors, authors_expected)
- categories = sorted([category.name
- for category, _ in generator.categories])
+ categories = sorted([category.name for category, _ in generator.categories])
categories_expected = [
- sorted(['Default', 'TestCategory', 'yeah', 'test', '指導書']),
- sorted(['Default', 'TestCategory', 'Yeah', 'test', '指導書'])]
+ sorted(["Default", "TestCategory", "yeah", "test", "指導書"]),
+ sorted(["Default", "TestCategory", "Yeah", "test", "指導書"]),
+ ]
self.assertIn(categories, categories_expected)
tags = sorted([tag.name for tag in generator.tags])
- tags_expected = sorted(['bar', 'foo', 'foobar', 'general', 'untagged',
- 'パイソン', 'マック'])
+ tags_expected = sorted(
+ ["bar", "foo", "foobar", "general", "untagged", "パイソン", "マック"]
+ )
self.assertEqual(tags, tags_expected)
def test_article_order_by(self):
settings = get_settings()
- settings['DEFAULT_CATEGORY'] = 'Default'
- settings['DEFAULT_DATE'] = (1970, 1, 1)
- settings['ARTICLE_ORDER_BY'] = 'title'
+ settings["DEFAULT_CATEGORY"] = "Default"
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
+ settings["ARTICLE_ORDER_BY"] = "title"
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
expected = [
- 'An Article With Code Block To Test Typogrify Ignore',
- 'Article title',
- 'Article with Nonconformant HTML meta tags',
- 'Article with an inline SVG',
- 'Article with markdown and empty tags',
- 'Article with markdown and nested summary metadata',
- 'Article with markdown and summary metadata multi',
- 'Article with markdown and summary metadata single',
- 'Article with markdown containing footnotes',
- 'Article with template',
- 'Metadata tags as list!',
- 'One -, two --, three --- dashes!',
- 'One -, two --, three --- dashes!',
- 'Rst with filename metadata',
- 'Test Markdown extensions',
- 'Test markdown File',
- 'Test md File',
- 'Test mdown File',
- 'Test metadata duplicates',
- 'Test mkd File',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is a super article !',
- 'This is an article with category !',
- ('This is an article with multiple authors in lastname, '
- 'firstname format!'),
- 'This is an article with multiple authors in list format!',
- 'This is an article with multiple authors!',
- 'This is an article with multiple authors!',
- 'This is an article without category !',
- 'This is an article without category !',
- 'マックOS X 10.8でパイソンとVirtualenvをインストールと設定']
+ "An Article With Code Block To Test Typogrify Ignore",
+ "Article title",
+ "Article with Nonconformant HTML meta tags",
+ "Article with an inline SVG",
+ "Article with markdown and empty tags",
+ "Article with markdown and nested summary metadata",
+ "Article with markdown and summary metadata multi",
+ "Article with markdown and summary metadata single",
+ "Article with markdown containing footnotes",
+ "Article with template",
+ "Metadata tags as list!",
+ "One -, two --, three --- dashes!",
+ "One -, two --, three --- dashes!",
+ "Rst with filename metadata",
+ "Test Markdown extensions",
+ "Test markdown File",
+ "Test md File",
+ "Test mdown File",
+ "Test metadata duplicates",
+ "Test mkd File",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is a super article !",
+ "This is an article with category !",
+ (
+ "This is an article with multiple authors in lastname, "
+ "firstname format!"
+ ),
+ "This is an article with multiple authors in list format!",
+ "This is an article with multiple authors!",
+ "This is an article with multiple authors!",
+ "This is an article without category !",
+ "This is an article without category !",
+ "マックOS X 10.8でパイソンとVirtualenvをインストールと設定",
+ ]
articles = [article.title for article in generator.articles]
self.assertEqual(articles, expected)
# reversed title
settings = get_settings()
- settings['DEFAULT_CATEGORY'] = 'Default'
- settings['DEFAULT_DATE'] = (1970, 1, 1)
- settings['ARTICLE_ORDER_BY'] = 'reversed-title'
+ settings["DEFAULT_CATEGORY"] = "Default"
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
+ settings["ARTICLE_ORDER_BY"] = "reversed-title"
context = get_context(settings)
generator = ArticlesGenerator(
- context=context, settings=settings,
- path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CONTENT_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
articles = [article.title for article in generator.articles]
@@ -750,7 +938,7 @@ class TestPageGenerator(unittest.TestCase):
# to match expected
def setUp(self):
- self.temp_cache = mkdtemp(prefix='pelican_cache.')
+ self.temp_cache = mkdtemp(prefix="pelican_cache.")
def tearDown(self):
rmtree(self.temp_cache)
@@ -760,112 +948,125 @@ class TestPageGenerator(unittest.TestCase):
def test_generate_context(self):
settings = get_settings()
- settings['CACHE_PATH'] = self.temp_cache
- settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
- settings['DEFAULT_DATE'] = (1970, 1, 1)
+ settings["CACHE_PATH"] = self.temp_cache
+ settings["PAGE_PATHS"] = ["TestPages"] # relative to CUR_DIR
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator(
- context=context, settings=settings,
- path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CUR_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
pages = self.distill_pages(generator.pages)
hidden_pages = self.distill_pages(generator.hidden_pages)
draft_pages = self.distill_pages(generator.draft_pages)
pages_expected = [
- ['This is a test page', 'published', 'page'],
- ['This is a markdown test page', 'published', 'page'],
- ['This is a test page with a preset template', 'published',
- 'custom'],
- ['Page with a bunch of links', 'published', 'page'],
- ['Page with static links', 'published', 'page'],
- ['A Page (Test) for sorting', 'published', 'page'],
+ ["This is a test page", "published", "page"],
+ ["This is a markdown test page", "published", "page"],
+ ["This is a test page with a preset template", "published", "custom"],
+ ["Page with a bunch of links", "published", "page"],
+ ["Page with static links", "published", "page"],
+ ["A Page (Test) for sorting", "published", "page"],
]
hidden_pages_expected = [
- ['This is a test hidden page', 'hidden', 'page'],
- ['This is a markdown test hidden page', 'hidden', 'page'],
- ['This is a test hidden page with a custom template', 'hidden',
- 'custom'],
+ ["This is a test hidden page", "hidden", "page"],
+ ["This is a markdown test hidden page", "hidden", "page"],
+ ["This is a test hidden page with a custom template", "hidden", "custom"],
]
draft_pages_expected = [
- ['This is a test draft page', 'draft', 'page'],
- ['This is a markdown test draft page', 'draft', 'page'],
- ['This is a test draft page with a custom template', 'draft',
- 'custom'],
+ ["This is a test draft page", "draft", "page"],
+ ["This is a markdown test draft page", "draft", "page"],
+ ["This is a test draft page with a custom template", "draft", "custom"],
]
self.assertEqual(sorted(pages_expected), sorted(pages))
self.assertEqual(
sorted(pages_expected),
- sorted(self.distill_pages(generator.context['pages'])))
+ sorted(self.distill_pages(generator.context["pages"])),
+ )
self.assertEqual(sorted(hidden_pages_expected), sorted(hidden_pages))
self.assertEqual(sorted(draft_pages_expected), sorted(draft_pages))
self.assertEqual(
sorted(hidden_pages_expected),
- sorted(self.distill_pages(generator.context['hidden_pages'])))
+ sorted(self.distill_pages(generator.context["hidden_pages"])),
+ )
self.assertEqual(
sorted(draft_pages_expected),
- sorted(self.distill_pages(generator.context['draft_pages'])))
+ sorted(self.distill_pages(generator.context["draft_pages"])),
+ )
def test_generate_sorted(self):
settings = get_settings()
- settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
- settings['CACHE_PATH'] = self.temp_cache
- settings['DEFAULT_DATE'] = (1970, 1, 1)
+ settings["PAGE_PATHS"] = ["TestPages"] # relative to CUR_DIR
+ settings["CACHE_PATH"] = self.temp_cache
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
context = get_context(settings)
# default sort (filename)
pages_expected_sorted_by_filename = [
- ['This is a test page', 'published', 'page'],
- ['This is a markdown test page', 'published', 'page'],
- ['A Page (Test) for sorting', 'published', 'page'],
- ['Page with a bunch of links', 'published', 'page'],
- ['Page with static links', 'published', 'page'],
- ['This is a test page with a preset template', 'published',
- 'custom'],
+ ["This is a test page", "published", "page"],
+ ["This is a markdown test page", "published", "page"],
+ ["A Page (Test) for sorting", "published", "page"],
+ ["Page with a bunch of links", "published", "page"],
+ ["Page with static links", "published", "page"],
+ ["This is a test page with a preset template", "published", "custom"],
]
generator = PagesGenerator(
- context=context, settings=settings,
- path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CUR_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
pages = self.distill_pages(generator.pages)
self.assertEqual(pages_expected_sorted_by_filename, pages)
# sort by title
pages_expected_sorted_by_title = [
- ['A Page (Test) for sorting', 'published', 'page'],
- ['Page with a bunch of links', 'published', 'page'],
- ['Page with static links', 'published', 'page'],
- ['This is a markdown test page', 'published', 'page'],
- ['This is a test page', 'published', 'page'],
- ['This is a test page with a preset template', 'published',
- 'custom'],
+ ["A Page (Test) for sorting", "published", "page"],
+ ["Page with a bunch of links", "published", "page"],
+ ["Page with static links", "published", "page"],
+ ["This is a markdown test page", "published", "page"],
+ ["This is a test page", "published", "page"],
+ ["This is a test page with a preset template", "published", "custom"],
]
- settings['PAGE_ORDER_BY'] = 'title'
+ settings["PAGE_ORDER_BY"] = "title"
context = get_context(settings)
generator = PagesGenerator(
- context=context.copy(), settings=settings,
- path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ context=context.copy(),
+ settings=settings,
+ path=CUR_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
pages = self.distill_pages(generator.pages)
self.assertEqual(pages_expected_sorted_by_title, pages)
# sort by title reversed
pages_expected_sorted_by_title = [
- ['This is a test page with a preset template', 'published',
- 'custom'],
- ['This is a test page', 'published', 'page'],
- ['This is a markdown test page', 'published', 'page'],
- ['Page with static links', 'published', 'page'],
- ['Page with a bunch of links', 'published', 'page'],
- ['A Page (Test) for sorting', 'published', 'page'],
+ ["This is a test page with a preset template", "published", "custom"],
+ ["This is a test page", "published", "page"],
+ ["This is a markdown test page", "published", "page"],
+ ["Page with static links", "published", "page"],
+ ["Page with a bunch of links", "published", "page"],
+ ["A Page (Test) for sorting", "published", "page"],
]
- settings['PAGE_ORDER_BY'] = 'reversed-title'
+ settings["PAGE_ORDER_BY"] = "reversed-title"
context = get_context(settings)
generator = PagesGenerator(
- context=context, settings=settings,
- path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CUR_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
pages = self.distill_pages(generator.pages)
self.assertEqual(pages_expected_sorted_by_title, pages)
@@ -876,18 +1077,22 @@ class TestPageGenerator(unittest.TestCase):
are generated correctly on pages
"""
settings = get_settings()
- settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
- settings['CACHE_PATH'] = self.temp_cache
- settings['DEFAULT_DATE'] = (1970, 1, 1)
+ settings["PAGE_PATHS"] = ["TestPages"] # relative to CUR_DIR
+ settings["CACHE_PATH"] = self.temp_cache
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator(
- context=context, settings=settings,
- path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CUR_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
pages_by_title = {p.title: p for p in generator.pages}
- test_content = pages_by_title['Page with a bunch of links'].content
+ test_content = pages_by_title["Page with a bunch of links"].content
self.assertIn('', test_content)
self.assertIn('', test_content)
@@ -897,80 +1102,80 @@ class TestPageGenerator(unittest.TestCase):
are included in context['static_links']
"""
settings = get_settings()
- settings['PAGE_PATHS'] = ['TestPages/page_with_static_links.md']
- settings['CACHE_PATH'] = self.temp_cache
- settings['DEFAULT_DATE'] = (1970, 1, 1)
+ settings["PAGE_PATHS"] = ["TestPages/page_with_static_links.md"]
+ settings["CACHE_PATH"] = self.temp_cache
+ settings["DEFAULT_DATE"] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator(
- context=context, settings=settings,
- path=CUR_DIR, theme=settings['THEME'], output_path=None)
+ context=context,
+ settings=settings,
+ path=CUR_DIR,
+ theme=settings["THEME"],
+ output_path=None,
+ )
generator.generate_context()
- self.assertIn('pelican/tests/TestPages/image0.jpg',
- context['static_links'])
- self.assertIn('pelican/tests/TestPages/image1.jpg',
- context['static_links'])
+ self.assertIn("pelican/tests/TestPages/image0.jpg", context["static_links"])
+ self.assertIn("pelican/tests/TestPages/image1.jpg", context["static_links"])
class TestTemplatePagesGenerator(TestCaseWithCLocale):
-
TEMPLATE_CONTENT = "foo: {{ foo }}"
def setUp(self):
super().setUp()
- self.temp_content = mkdtemp(prefix='pelicantests.')
- self.temp_output = mkdtemp(prefix='pelicantests.')
+ self.temp_content = mkdtemp(prefix="pelicantests.")
+ self.temp_output = mkdtemp(prefix="pelicantests.")
def tearDown(self):
rmtree(self.temp_content)
rmtree(self.temp_output)
def test_generate_output(self):
-
settings = get_settings()
- settings['STATIC_PATHS'] = ['static']
- settings['TEMPLATE_PAGES'] = {
- 'template/source.html': 'generated/file.html'
- }
+ settings["STATIC_PATHS"] = ["static"]
+ settings["TEMPLATE_PAGES"] = {"template/source.html": "generated/file.html"}
generator = TemplatePagesGenerator(
- context={'foo': 'bar'}, settings=settings,
- path=self.temp_content, theme='', output_path=self.temp_output)
+ context={"foo": "bar"},
+ settings=settings,
+ path=self.temp_content,
+ theme="",
+ output_path=self.temp_output,
+ )
# create a dummy template file
- template_dir = os.path.join(self.temp_content, 'template')
- template_path = os.path.join(template_dir, 'source.html')
+ template_dir = os.path.join(self.temp_content, "template")
+ template_path = os.path.join(template_dir, "source.html")
os.makedirs(template_dir)
- with open(template_path, 'w') as template_file:
+ with open(template_path, "w") as template_file:
template_file.write(self.TEMPLATE_CONTENT)
writer = Writer(self.temp_output, settings=settings)
generator.generate_output(writer)
- output_path = os.path.join(self.temp_output, 'generated', 'file.html')
+ output_path = os.path.join(self.temp_output, "generated", "file.html")
# output file has been generated
self.assertTrue(os.path.exists(output_path))
# output content is correct
with open(output_path) as output_file:
- self.assertEqual(output_file.read(), 'foo: bar')
+ self.assertEqual(output_file.read(), "foo: bar")
class TestStaticGenerator(unittest.TestCase):
-
def setUp(self):
- self.content_path = os.path.join(CUR_DIR, 'mixed_content')
- self.temp_content = mkdtemp(prefix='testcontent.')
- self.temp_output = mkdtemp(prefix='testoutput.')
+ self.content_path = os.path.join(CUR_DIR, "mixed_content")
+ self.temp_content = mkdtemp(prefix="testcontent.")
+ self.temp_output = mkdtemp(prefix="testoutput.")
self.settings = get_settings()
- self.settings['PATH'] = self.temp_content
- self.settings['STATIC_PATHS'] = ["static"]
- self.settings['OUTPUT_PATH'] = self.temp_output
+ self.settings["PATH"] = self.temp_content
+ self.settings["STATIC_PATHS"] = ["static"]
+ self.settings["OUTPUT_PATH"] = self.temp_output
os.mkdir(os.path.join(self.temp_content, "static"))
- self.startfile = os.path.join(self.temp_content,
- "static", "staticfile")
+ self.startfile = os.path.join(self.temp_content, "static", "staticfile")
self.endfile = os.path.join(self.temp_output, "static", "staticfile")
self.generator = StaticGenerator(
context=get_context(),
@@ -978,7 +1183,7 @@ class TestStaticGenerator(unittest.TestCase):
path=self.temp_content,
theme="",
output_path=self.temp_output,
- )
+ )
def tearDown(self):
rmtree(self.temp_content)
@@ -989,155 +1194,198 @@ class TestStaticGenerator(unittest.TestCase):
def test_theme_static_paths_dirs(self):
"""Test that StaticGenerator properly copies also files mentioned in
- TEMPLATE_STATIC_PATHS, not just directories."""
+ TEMPLATE_STATIC_PATHS, not just directories."""
settings = get_settings(PATH=self.content_path)
context = get_context(settings, staticfiles=[])
StaticGenerator(
- context=context, settings=settings,
- path=settings['PATH'], output_path=self.temp_output,
- theme=settings['THEME']).generate_output(None)
+ context=context,
+ settings=settings,
+ path=settings["PATH"],
+ output_path=self.temp_output,
+ theme=settings["THEME"],
+ ).generate_output(None)
# The content of dirs listed in THEME_STATIC_PATHS (defaulting to
# "static") is put into the output
- self.assertTrue(os.path.isdir(os.path.join(self.temp_output,
- "theme/css/")))
- self.assertTrue(os.path.isdir(os.path.join(self.temp_output,
- "theme/fonts/")))
+ self.assertTrue(os.path.isdir(os.path.join(self.temp_output, "theme/css/")))
+ self.assertTrue(os.path.isdir(os.path.join(self.temp_output, "theme/fonts/")))
def test_theme_static_paths_files(self):
"""Test that StaticGenerator properly copies also files mentioned in
- TEMPLATE_STATIC_PATHS, not just directories."""
+ TEMPLATE_STATIC_PATHS, not just directories."""
settings = get_settings(
PATH=self.content_path,
- THEME_STATIC_PATHS=['static/css/fonts.css', 'static/fonts/'],)
+ THEME_STATIC_PATHS=["static/css/fonts.css", "static/fonts/"],
+ )
context = get_context(settings, staticfiles=[])
StaticGenerator(
- context=context, settings=settings,
- path=settings['PATH'], output_path=self.temp_output,
- theme=settings['THEME']).generate_output(None)
+ context=context,
+ settings=settings,
+ path=settings["PATH"],
+ output_path=self.temp_output,
+ theme=settings["THEME"],
+ ).generate_output(None)
# Only the content of dirs and files listed in THEME_STATIC_PATHS are
# put into the output, not everything from static/
- self.assertFalse(os.path.isdir(os.path.join(self.temp_output,
- "theme/css/")))
- self.assertFalse(os.path.isdir(os.path.join(self.temp_output,
- "theme/fonts/")))
+ self.assertFalse(os.path.isdir(os.path.join(self.temp_output, "theme/css/")))
+ self.assertFalse(os.path.isdir(os.path.join(self.temp_output, "theme/fonts/")))
- self.assertTrue(os.path.isfile(os.path.join(
- self.temp_output, "theme/Yanone_Kaffeesatz_400.eot")))
- self.assertTrue(os.path.isfile(os.path.join(
- self.temp_output, "theme/Yanone_Kaffeesatz_400.svg")))
- self.assertTrue(os.path.isfile(os.path.join(
- self.temp_output, "theme/Yanone_Kaffeesatz_400.ttf")))
- self.assertTrue(os.path.isfile(os.path.join(
- self.temp_output, "theme/Yanone_Kaffeesatz_400.woff")))
- self.assertTrue(os.path.isfile(os.path.join(
- self.temp_output, "theme/Yanone_Kaffeesatz_400.woff2")))
- self.assertTrue(os.path.isfile(os.path.join(self.temp_output,
- "theme/font.css")))
- self.assertTrue(os.path.isfile(os.path.join(self.temp_output,
- "theme/fonts.css")))
+ self.assertTrue(
+ os.path.isfile(
+ os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.eot")
+ )
+ )
+ self.assertTrue(
+ os.path.isfile(
+ os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.svg")
+ )
+ )
+ self.assertTrue(
+ os.path.isfile(
+ os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.ttf")
+ )
+ )
+ self.assertTrue(
+ os.path.isfile(
+ os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.woff")
+ )
+ )
+ self.assertTrue(
+ os.path.isfile(
+ os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.woff2")
+ )
+ )
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.temp_output, "theme/font.css"))
+ )
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.temp_output, "theme/fonts.css"))
+ )
def test_static_excludes(self):
- """Test that StaticGenerator respects STATIC_EXCLUDES.
- """
+ """Test that StaticGenerator respects STATIC_EXCLUDES."""
settings = get_settings(
- STATIC_EXCLUDES=['subdir'],
+ STATIC_EXCLUDES=["subdir"],
PATH=self.content_path,
- STATIC_PATHS=[''],)
+ STATIC_PATHS=[""],
+ )
context = get_context(settings)
StaticGenerator(
- context=context, settings=settings,
- path=settings['PATH'], output_path=self.temp_output,
- theme=settings['THEME']).generate_context()
+ context=context,
+ settings=settings,
+ path=settings["PATH"],
+ output_path=self.temp_output,
+ theme=settings["THEME"],
+ ).generate_context()
- staticnames = [os.path.basename(c.source_path)
- for c in context['staticfiles']]
+ staticnames = [os.path.basename(c.source_path) for c in context["staticfiles"]]
self.assertNotIn(
- 'subdir_fake_image.jpg', staticnames,
- "StaticGenerator processed a file in a STATIC_EXCLUDES directory")
+ "subdir_fake_image.jpg",
+ staticnames,
+ "StaticGenerator processed a file in a STATIC_EXCLUDES directory",
+ )
self.assertIn(
- 'fake_image.jpg', staticnames,
- "StaticGenerator skipped a file that it should have included")
+ "fake_image.jpg",
+ staticnames,
+ "StaticGenerator skipped a file that it should have included",
+ )
def test_static_exclude_sources(self):
- """Test that StaticGenerator respects STATIC_EXCLUDE_SOURCES.
- """
+ """Test that StaticGenerator respects STATIC_EXCLUDE_SOURCES."""
settings = get_settings(
STATIC_EXCLUDE_SOURCES=True,
PATH=self.content_path,
- PAGE_PATHS=[''],
- STATIC_PATHS=[''],
- CACHE_CONTENT=False,)
+ PAGE_PATHS=[""],
+ STATIC_PATHS=[""],
+ CACHE_CONTENT=False,
+ )
context = get_context(settings)
for generator_class in (PagesGenerator, StaticGenerator):
generator_class(
- context=context, settings=settings,
- path=settings['PATH'], output_path=self.temp_output,
- theme=settings['THEME']).generate_context()
+ context=context,
+ settings=settings,
+ path=settings["PATH"],
+ output_path=self.temp_output,
+ theme=settings["THEME"],
+ ).generate_context()
- staticnames = [os.path.basename(c.source_path)
- for c in context['staticfiles']]
+ staticnames = [os.path.basename(c.source_path) for c in context["staticfiles"]]
self.assertFalse(
any(name.endswith(".md") for name in staticnames),
- "STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file")
+ "STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file",
+ )
settings.update(STATIC_EXCLUDE_SOURCES=False)
context = get_context(settings)
for generator_class in (PagesGenerator, StaticGenerator):
generator_class(
- context=context, settings=settings,
- path=settings['PATH'], output_path=self.temp_output,
- theme=settings['THEME']).generate_context()
+ context=context,
+ settings=settings,
+ path=settings["PATH"],
+ output_path=self.temp_output,
+ theme=settings["THEME"],
+ ).generate_context()
- staticnames = [os.path.basename(c.source_path)
- for c in context['staticfiles']]
+ staticnames = [os.path.basename(c.source_path) for c in context["staticfiles"]]
self.assertTrue(
any(name.endswith(".md") for name in staticnames),
- "STATIC_EXCLUDE_SOURCES=False failed to include a markdown file")
+ "STATIC_EXCLUDE_SOURCES=False failed to include a markdown file",
+ )
def test_static_links(self):
- """Test that StaticGenerator uses files in static_links
- """
+ """Test that StaticGenerator uses files in static_links"""
settings = get_settings(
- STATIC_EXCLUDES=['subdir'],
+ STATIC_EXCLUDES=["subdir"],
PATH=self.content_path,
- STATIC_PATHS=[],)
+ STATIC_PATHS=[],
+ )
context = get_context(settings)
- context['static_links'] |= {'short_page.md', 'subdir_fake_image.jpg'}
+ context["static_links"] |= {"short_page.md", "subdir_fake_image.jpg"}
StaticGenerator(
- context=context, settings=settings,
- path=settings['PATH'], output_path=self.temp_output,
- theme=settings['THEME']).generate_context()
+ context=context,
+ settings=settings,
+ path=settings["PATH"],
+ output_path=self.temp_output,
+ theme=settings["THEME"],
+ ).generate_context()
staticfiles_names = [
- os.path.basename(c.source_path) for c in context['staticfiles']]
+ os.path.basename(c.source_path) for c in context["staticfiles"]
+ ]
- static_content_names = [
- os.path.basename(c) for c in context['static_content']]
+ static_content_names = [os.path.basename(c) for c in context["static_content"]]
self.assertIn(
- 'short_page.md', staticfiles_names,
- "StaticGenerator skipped a file that it should have included")
+ "short_page.md",
+ staticfiles_names,
+ "StaticGenerator skipped a file that it should have included",
+ )
self.assertIn(
- 'short_page.md', static_content_names,
- "StaticGenerator skipped a file that it should have included")
+ "short_page.md",
+ static_content_names,
+ "StaticGenerator skipped a file that it should have included",
+ )
self.assertIn(
- 'subdir_fake_image.jpg', staticfiles_names,
- "StaticGenerator skipped a file that it should have included")
+ "subdir_fake_image.jpg",
+ staticfiles_names,
+ "StaticGenerator skipped a file that it should have included",
+ )
self.assertIn(
- 'subdir_fake_image.jpg', static_content_names,
- "StaticGenerator skipped a file that it should have included")
+ "subdir_fake_image.jpg",
+ static_content_names,
+ "StaticGenerator skipped a file that it should have included",
+ )
def test_copy_one_file(self):
with open(self.startfile, "w") as f:
@@ -1160,7 +1408,7 @@ class TestStaticGenerator(unittest.TestCase):
staticfile = MagicMock()
staticfile.source_path = self.startfile
staticfile.save_as = self.endfile
- self.settings['STATIC_CHECK_IF_MODIFIED'] = True
+ self.settings["STATIC_CHECK_IF_MODIFIED"] = True
with open(staticfile.source_path, "w") as f:
f.write("a")
os.mkdir(os.path.join(self.temp_output, "static"))
@@ -1181,7 +1429,7 @@ class TestStaticGenerator(unittest.TestCase):
self.assertTrue(isnewer)
def test_skip_file_when_source_is_not_newer(self):
- self.settings['STATIC_CHECK_IF_MODIFIED'] = True
+ self.settings["STATIC_CHECK_IF_MODIFIED"] = True
with open(self.startfile, "w") as f:
f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static"))
@@ -1201,7 +1449,7 @@ class TestStaticGenerator(unittest.TestCase):
self.assertFalse(os.path.samefile(self.startfile, self.endfile))
def test_output_file_is_linked_to_source(self):
- self.settings['STATIC_CREATE_LINKS'] = True
+ self.settings["STATIC_CREATE_LINKS"] = True
with open(self.startfile, "w") as f:
f.write("staticcontent")
self.generator.generate_context()
@@ -1209,7 +1457,7 @@ class TestStaticGenerator(unittest.TestCase):
self.assertTrue(os.path.samefile(self.startfile, self.endfile))
def test_output_file_exists_and_is_newer(self):
- self.settings['STATIC_CREATE_LINKS'] = True
+ self.settings["STATIC_CREATE_LINKS"] = True
with open(self.startfile, "w") as f:
f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static"))
@@ -1219,9 +1467,9 @@ class TestStaticGenerator(unittest.TestCase):
self.generator.generate_output(None)
self.assertTrue(os.path.samefile(self.startfile, self.endfile))
- @unittest.skipUnless(can_symlink(), 'No symlink privilege')
+ @unittest.skipUnless(can_symlink(), "No symlink privilege")
def test_can_symlink_when_hardlink_not_possible(self):
- self.settings['STATIC_CREATE_LINKS'] = True
+ self.settings["STATIC_CREATE_LINKS"] = True
with open(self.startfile, "w") as f:
f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static"))
@@ -1230,9 +1478,9 @@ class TestStaticGenerator(unittest.TestCase):
self.generator.generate_output(None)
self.assertTrue(os.path.islink(self.endfile))
- @unittest.skipUnless(can_symlink(), 'No symlink privilege')
+ @unittest.skipUnless(can_symlink(), "No symlink privilege")
def test_existing_symlink_is_considered_up_to_date(self):
- self.settings['STATIC_CREATE_LINKS'] = True
+ self.settings["STATIC_CREATE_LINKS"] = True
with open(self.startfile, "w") as f:
f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static"))
@@ -1243,9 +1491,9 @@ class TestStaticGenerator(unittest.TestCase):
requires_update = self.generator._file_update_required(staticfile)
self.assertFalse(requires_update)
- @unittest.skipUnless(can_symlink(), 'No symlink privilege')
+ @unittest.skipUnless(can_symlink(), "No symlink privilege")
def test_invalid_symlink_is_overwritten(self):
- self.settings['STATIC_CREATE_LINKS'] = True
+ self.settings["STATIC_CREATE_LINKS"] = True
with open(self.startfile, "w") as f:
f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static"))
@@ -1263,14 +1511,14 @@ class TestStaticGenerator(unittest.TestCase):
# os.path.realpath is broken on Windows before python3.8 for symlinks.
# This is a (ugly) workaround.
# see: https://bugs.python.org/issue9949
- if os.name == 'nt' and sys.version_info < (3, 8):
+ if os.name == "nt" and sys.version_info < (3, 8):
+
def get_real_path(path):
return os.readlink(path) if os.path.islink(path) else path
else:
get_real_path = os.path.realpath
- self.assertEqual(get_real_path(self.endfile),
- get_real_path(self.startfile))
+ self.assertEqual(get_real_path(self.endfile), get_real_path(self.startfile))
def test_delete_existing_file_before_mkdir(self):
with open(self.startfile, "w") as f:
@@ -1279,16 +1527,14 @@ class TestStaticGenerator(unittest.TestCase):
f.write("This file should be a directory")
self.generator.generate_context()
self.generator.generate_output(None)
- self.assertTrue(
- os.path.isdir(os.path.join(self.temp_output, "static")))
+ self.assertTrue(os.path.isdir(os.path.join(self.temp_output, "static")))
self.assertTrue(os.path.isfile(self.endfile))
class TestJinja2Environment(TestCaseWithCLocale):
-
def setUp(self):
- self.temp_content = mkdtemp(prefix='pelicantests.')
- self.temp_output = mkdtemp(prefix='pelicantests.')
+ self.temp_content = mkdtemp(prefix="pelicantests.")
+ self.temp_output = mkdtemp(prefix="pelicantests.")
def tearDown(self):
rmtree(self.temp_content)
@@ -1296,27 +1542,29 @@ class TestJinja2Environment(TestCaseWithCLocale):
def _test_jinja2_helper(self, additional_settings, content, expected):
settings = get_settings()
- settings['STATIC_PATHS'] = ['static']
- settings['TEMPLATE_PAGES'] = {
- 'template/source.html': 'generated/file.html'
- }
+ settings["STATIC_PATHS"] = ["static"]
+ settings["TEMPLATE_PAGES"] = {"template/source.html": "generated/file.html"}
settings.update(additional_settings)
generator = TemplatePagesGenerator(
- context={'foo': 'foo', 'bar': 'bar'}, settings=settings,
- path=self.temp_content, theme='', output_path=self.temp_output)
+ context={"foo": "foo", "bar": "bar"},
+ settings=settings,
+ path=self.temp_content,
+ theme="",
+ output_path=self.temp_output,
+ )
# create a dummy template file
- template_dir = os.path.join(self.temp_content, 'template')
- template_path = os.path.join(template_dir, 'source.html')
+ template_dir = os.path.join(self.temp_content, "template")
+ template_path = os.path.join(template_dir, "source.html")
os.makedirs(template_dir)
- with open(template_path, 'w') as template_file:
+ with open(template_path, "w") as template_file:
template_file.write(content)
writer = Writer(self.temp_output, settings=settings)
generator.generate_output(writer)
- output_path = os.path.join(self.temp_output, 'generated', 'file.html')
+ output_path = os.path.join(self.temp_output, "generated", "file.html")
# output file has been generated
self.assertTrue(os.path.exists(output_path))
@@ -1327,32 +1575,32 @@ class TestJinja2Environment(TestCaseWithCLocale):
def test_jinja2_filter(self):
"""JINJA_FILTERS adds custom filters to Jinja2 environment"""
- content = 'foo: {{ foo|custom_filter }}, bar: {{ bar|custom_filter }}'
- settings = {'JINJA_FILTERS': {'custom_filter': lambda x: x.upper()}}
- expected = 'foo: FOO, bar: BAR'
+ content = "foo: {{ foo|custom_filter }}, bar: {{ bar|custom_filter }}"
+ settings = {"JINJA_FILTERS": {"custom_filter": lambda x: x.upper()}}
+ expected = "foo: FOO, bar: BAR"
self._test_jinja2_helper(settings, content, expected)
def test_jinja2_test(self):
"""JINJA_TESTS adds custom tests to Jinja2 environment"""
- content = 'foo {{ foo is custom_test }}, bar {{ bar is custom_test }}'
- settings = {'JINJA_TESTS': {'custom_test': lambda x: x == 'bar'}}
- expected = 'foo False, bar True'
+ content = "foo {{ foo is custom_test }}, bar {{ bar is custom_test }}"
+ settings = {"JINJA_TESTS": {"custom_test": lambda x: x == "bar"}}
+ expected = "foo False, bar True"
self._test_jinja2_helper(settings, content, expected)
def test_jinja2_global(self):
"""JINJA_GLOBALS adds custom globals to Jinja2 environment"""
- content = '{{ custom_global }}'
- settings = {'JINJA_GLOBALS': {'custom_global': 'foobar'}}
- expected = 'foobar'
+ content = "{{ custom_global }}"
+ settings = {"JINJA_GLOBALS": {"custom_global": "foobar"}}
+ expected = "foobar"
self._test_jinja2_helper(settings, content, expected)
def test_jinja2_extension(self):
"""JINJA_ENVIRONMENT adds extensions to Jinja2 environment"""
- content = '{% set stuff = [] %}{% do stuff.append(1) %}{{ stuff }}'
- settings = {'JINJA_ENVIRONMENT': {'extensions': ['jinja2.ext.do']}}
- expected = '[1]'
+ content = "{% set stuff = [] %}{% do stuff.append(1) %}{{ stuff }}"
+ settings = {"JINJA_ENVIRONMENT": {"extensions": ["jinja2.ext.do"]}}
+ expected = "[1]"
self._test_jinja2_helper(settings, content, expected)
diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py
index 870d3001..05ef5bbd 100644
--- a/pelican/tests/test_importer.py
+++ b/pelican/tests/test_importer.py
@@ -4,26 +4,35 @@ from posixpath import join as posix_join
from unittest.mock import patch
from pelican.settings import DEFAULT_CONFIG
-from pelican.tests.support import (mute, skipIfNoExecutable, temporary_folder,
- unittest, TestCaseWithCLocale)
-from pelican.tools.pelican_import import (blogger2fields, build_header,
- build_markdown_header,
- decode_wp_content,
- download_attachments, fields2pelican,
- get_attachments, tumblr2fields,
- wp2fields,
- )
+from pelican.tests.support import (
+ mute,
+ skipIfNoExecutable,
+ temporary_folder,
+ unittest,
+ TestCaseWithCLocale,
+)
+from pelican.tools.pelican_import import (
+ blogger2fields,
+ build_header,
+ build_markdown_header,
+ decode_wp_content,
+ download_attachments,
+ fields2pelican,
+ get_attachments,
+ tumblr2fields,
+ wp2fields,
+)
from pelican.utils import path_to_file_url, slugify
CUR_DIR = os.path.abspath(os.path.dirname(__file__))
-BLOGGER_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'bloggerexport.xml')
-WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'wordpressexport.xml')
-WORDPRESS_ENCODED_CONTENT_SAMPLE = os.path.join(CUR_DIR,
- 'content',
- 'wordpress_content_encoded')
-WORDPRESS_DECODED_CONTENT_SAMPLE = os.path.join(CUR_DIR,
- 'content',
- 'wordpress_content_decoded')
+BLOGGER_XML_SAMPLE = os.path.join(CUR_DIR, "content", "bloggerexport.xml")
+WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, "content", "wordpressexport.xml")
+WORDPRESS_ENCODED_CONTENT_SAMPLE = os.path.join(
+ CUR_DIR, "content", "wordpress_content_encoded"
+)
+WORDPRESS_DECODED_CONTENT_SAMPLE = os.path.join(
+ CUR_DIR, "content", "wordpress_content_decoded"
+)
try:
from bs4 import BeautifulSoup
@@ -36,10 +45,9 @@ except ImportError:
LXML = False
-@skipIfNoExecutable(['pandoc', '--version'])
-@unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module')
+@skipIfNoExecutable(["pandoc", "--version"])
+@unittest.skipUnless(BeautifulSoup, "Needs BeautifulSoup module")
class TestBloggerXmlImporter(TestCaseWithCLocale):
-
def setUp(self):
super().setUp()
self.posts = blogger2fields(BLOGGER_XML_SAMPLE)
@@ -50,16 +58,17 @@ class TestBloggerXmlImporter(TestCaseWithCLocale):
"""
test_posts = list(self.posts)
kinds = {x[8] for x in test_posts}
- self.assertEqual({'page', 'article', 'comment'}, kinds)
- page_titles = {x[0] for x in test_posts if x[8] == 'page'}
- self.assertEqual({'Test page', 'Test page 2'}, page_titles)
- article_titles = {x[0] for x in test_posts if x[8] == 'article'}
- self.assertEqual({'Black as Egypt\'s Night', 'The Steel Windpipe'},
- article_titles)
- comment_titles = {x[0] for x in test_posts if x[8] == 'comment'}
- self.assertEqual({'Mishka, always a pleasure to read your '
- 'adventures!...'},
- comment_titles)
+ self.assertEqual({"page", "article", "comment"}, kinds)
+ page_titles = {x[0] for x in test_posts if x[8] == "page"}
+ self.assertEqual({"Test page", "Test page 2"}, page_titles)
+ article_titles = {x[0] for x in test_posts if x[8] == "article"}
+ self.assertEqual(
+ {"Black as Egypt's Night", "The Steel Windpipe"}, article_titles
+ )
+ comment_titles = {x[0] for x in test_posts if x[8] == "comment"}
+ self.assertEqual(
+ {"Mishka, always a pleasure to read your " "adventures!..."}, comment_titles
+ )
def test_recognise_status_with_correct_filename(self):
"""Check that importerer outputs only statuses 'published' and 'draft',
@@ -67,24 +76,25 @@ class TestBloggerXmlImporter(TestCaseWithCLocale):
"""
test_posts = list(self.posts)
statuses = {x[7] for x in test_posts}
- self.assertEqual({'published', 'draft'}, statuses)
+ self.assertEqual({"published", "draft"}, statuses)
- draft_filenames = {x[2] for x in test_posts if x[7] == 'draft'}
+ draft_filenames = {x[2] for x in test_posts if x[7] == "draft"}
# draft filenames are id-based
- self.assertEqual({'page-4386962582497458967',
- 'post-1276418104709695660'}, draft_filenames)
+ self.assertEqual(
+ {"page-4386962582497458967", "post-1276418104709695660"}, draft_filenames
+ )
- published_filenames = {x[2] for x in test_posts if x[7] == 'published'}
+ published_filenames = {x[2] for x in test_posts if x[7] == "published"}
# published filenames are url-based, except comments
- self.assertEqual({'the-steel-windpipe',
- 'test-page',
- 'post-5590533389087749201'}, published_filenames)
+ self.assertEqual(
+ {"the-steel-windpipe", "test-page", "post-5590533389087749201"},
+ published_filenames,
+ )
-@skipIfNoExecutable(['pandoc', '--version'])
-@unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module')
+@skipIfNoExecutable(["pandoc", "--version"])
+@unittest.skipUnless(BeautifulSoup, "Needs BeautifulSoup module")
class TestWordpressXmlImporter(TestCaseWithCLocale):
-
def setUp(self):
super().setUp()
self.posts = wp2fields(WORDPRESS_XML_SAMPLE)
@@ -92,30 +102,49 @@ class TestWordpressXmlImporter(TestCaseWithCLocale):
def test_ignore_empty_posts(self):
self.assertTrue(self.posts)
- for (title, content, fname, date, author,
- categ, tags, status, kind, format) in self.posts:
+ for (
+ title,
+ content,
+ fname,
+ date,
+ author,
+ categ,
+ tags,
+ status,
+ kind,
+ format,
+ ) in self.posts:
self.assertTrue(title.strip())
def test_recognise_page_kind(self):
- """ Check that we recognise pages in wordpress, as opposed to posts """
+ """Check that we recognise pages in wordpress, as opposed to posts"""
self.assertTrue(self.posts)
# Collect (title, filename, kind) of non-empty posts recognised as page
pages_data = []
- for (title, content, fname, date, author,
- categ, tags, status, kind, format) in self.posts:
- if kind == 'page':
+ for (
+ title,
+ content,
+ fname,
+ date,
+ author,
+ categ,
+ tags,
+ status,
+ kind,
+ format,
+ ) in self.posts:
+ if kind == "page":
pages_data.append((title, fname))
self.assertEqual(2, len(pages_data))
- self.assertEqual(('Page', 'contact'), pages_data[0])
- self.assertEqual(('Empty Page', 'empty'), pages_data[1])
+ self.assertEqual(("Page", "contact"), pages_data[0])
+ self.assertEqual(("Empty Page", "empty"), pages_data[1])
def test_dirpage_directive_for_page_kind(self):
silent_f2p = mute(True)(fields2pelican)
test_post = filter(lambda p: p[0].startswith("Empty Page"), self.posts)
with temporary_folder() as temp:
- fname = list(silent_f2p(test_post, 'markdown',
- temp, dirpage=True))[0]
- self.assertTrue(fname.endswith('pages%sempty.md' % os.path.sep))
+ fname = list(silent_f2p(test_post, "markdown", temp, dirpage=True))[0]
+ self.assertTrue(fname.endswith("pages%sempty.md" % os.path.sep))
def test_dircat(self):
silent_f2p = mute(True)(fields2pelican)
@@ -125,14 +154,13 @@ class TestWordpressXmlImporter(TestCaseWithCLocale):
if len(post[5]) > 0: # Has a category
test_posts.append(post)
with temporary_folder() as temp:
- fnames = list(silent_f2p(test_posts, 'markdown',
- temp, dircat=True))
- subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS']
+ fnames = list(silent_f2p(test_posts, "markdown", temp, dircat=True))
+ subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"]
index = 0
for post in test_posts:
name = post[2]
category = slugify(post[5][0], regex_subs=subs, preserve_case=True)
- name += '.md'
+ name += ".md"
filename = os.path.join(category, name)
out_name = fnames[index]
self.assertTrue(out_name.endswith(filename))
@@ -141,9 +169,19 @@ class TestWordpressXmlImporter(TestCaseWithCLocale):
def test_unless_custom_post_all_items_should_be_pages_or_posts(self):
self.assertTrue(self.posts)
pages_data = []
- for (title, content, fname, date, author, categ,
- tags, status, kind, format) in self.posts:
- if kind == 'page' or kind == 'article':
+ for (
+ title,
+ content,
+ fname,
+ date,
+ author,
+ categ,
+ tags,
+ status,
+ kind,
+ format,
+ ) in self.posts:
+ if kind == "page" or kind == "article":
pass
else:
pages_data.append((title, fname))
@@ -152,40 +190,45 @@ class TestWordpressXmlImporter(TestCaseWithCLocale):
def test_recognise_custom_post_type(self):
self.assertTrue(self.custposts)
cust_data = []
- for (title, content, fname, date, author, categ,
- tags, status, kind, format) in self.custposts:
- if kind == 'article' or kind == 'page':
+ for (
+ title,
+ content,
+ fname,
+ date,
+ author,
+ categ,
+ tags,
+ status,
+ kind,
+ format,
+ ) in self.custposts:
+ if kind == "article" or kind == "page":
pass
else:
cust_data.append((title, kind))
self.assertEqual(3, len(cust_data))
+ self.assertEqual(("A custom post in category 4", "custom1"), cust_data[0])
+ self.assertEqual(("A custom post in category 5", "custom1"), cust_data[1])
self.assertEqual(
- ('A custom post in category 4', 'custom1'),
- cust_data[0])
- self.assertEqual(
- ('A custom post in category 5', 'custom1'),
- cust_data[1])
- self.assertEqual(
- ('A 2nd custom post type also in category 5', 'custom2'),
- cust_data[2])
+ ("A 2nd custom post type also in category 5", "custom2"), cust_data[2]
+ )
def test_custom_posts_put_in_own_dir(self):
silent_f2p = mute(True)(fields2pelican)
test_posts = []
for post in self.custposts:
# check post kind
- if post[8] == 'article' or post[8] == 'page':
+ if post[8] == "article" or post[8] == "page":
pass
else:
test_posts.append(post)
with temporary_folder() as temp:
- fnames = list(silent_f2p(test_posts, 'markdown',
- temp, wp_custpost=True))
+ fnames = list(silent_f2p(test_posts, "markdown", temp, wp_custpost=True))
index = 0
for post in test_posts:
name = post[2]
kind = post[8]
- name += '.md'
+ name += ".md"
filename = os.path.join(kind, name)
out_name = fnames[index]
self.assertTrue(out_name.endswith(filename))
@@ -196,20 +239,21 @@ class TestWordpressXmlImporter(TestCaseWithCLocale):
test_posts = []
for post in self.custposts:
# check post kind
- if post[8] == 'article' or post[8] == 'page':
+ if post[8] == "article" or post[8] == "page":
pass
else:
test_posts.append(post)
with temporary_folder() as temp:
- fnames = list(silent_f2p(test_posts, 'markdown', temp,
- wp_custpost=True, dircat=True))
- subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS']
+ fnames = list(
+ silent_f2p(test_posts, "markdown", temp, wp_custpost=True, dircat=True)
+ )
+ subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"]
index = 0
for post in test_posts:
name = post[2]
kind = post[8]
category = slugify(post[5][0], regex_subs=subs, preserve_case=True)
- name += '.md'
+ name += ".md"
filename = os.path.join(kind, category, name)
out_name = fnames[index]
self.assertTrue(out_name.endswith(filename))
@@ -221,16 +265,19 @@ class TestWordpressXmlImporter(TestCaseWithCLocale):
test_posts = []
for post in self.custposts:
# check post kind
- if post[8] == 'page':
+ if post[8] == "page":
test_posts.append(post)
with temporary_folder() as temp:
- fnames = list(silent_f2p(test_posts, 'markdown', temp,
- wp_custpost=True, dirpage=False))
+ fnames = list(
+ silent_f2p(
+ test_posts, "markdown", temp, wp_custpost=True, dirpage=False
+ )
+ )
index = 0
for post in test_posts:
name = post[2]
- name += '.md'
- filename = os.path.join('pages', name)
+ name += ".md"
+ filename = os.path.join("pages", name)
out_name = fnames[index]
self.assertFalse(out_name.endswith(filename))
@@ -238,117 +285,114 @@ class TestWordpressXmlImporter(TestCaseWithCLocale):
test_posts = list(self.posts)
def r(f):
- with open(f, encoding='utf-8') as infile:
+ with open(f, encoding="utf-8") as infile:
return infile.read()
+
silent_f2p = mute(True)(fields2pelican)
with temporary_folder() as temp:
-
- rst_files = (r(f) for f
- in silent_f2p(test_posts, 'markdown', temp))
- self.assertTrue(any('
Now with added support for TLA.
\n') + 'acronym">TLA.\n' + ) self.assertEqual(page.content, expected) except ImportError: - return unittest.skip('need the typogrify distribution') + return unittest.skip("need the typogrify distribution") def test_typogrify_summary(self): # if nothing is specified in the settings, the summary should be # unmodified - page = self.read_file(path='article_with_metadata.rst') - expected = ('Multi-line metadata should be' - ' supported\nas well as inline' - ' markup and stuff to "typogrify' - '"...
\n') + page = self.read_file(path="article_with_metadata.rst") + expected = ( + 'Multi-line metadata should be' + " supported\nas well as inline" + " markup and stuff to "typogrify" + ""...
\n" + ) - self.assertEqual(page.metadata['summary'], expected) + self.assertEqual(page.metadata["summary"], expected) try: # otherwise, typogrify should be applied - page = self.read_file(path='article_with_metadata.rst', - TYPOGRIFY=True) - expected = ('Multi-line metadata should be' - ' supported\nas well as inline' - ' markup and stuff to “typogrify' - '”…
\n') + page = self.read_file(path="article_with_metadata.rst", TYPOGRIFY=True) + expected = ( + 'Multi-line metadata should be' + " supported\nas well as inline" + " markup and stuff to “typogrify" + "”…
\n" + ) - self.assertEqual(page.metadata['summary'], expected) + self.assertEqual(page.metadata["summary"], expected) except ImportError: - return unittest.skip('need the typogrify distribution') + return unittest.skip("need the typogrify distribution") def test_typogrify_ignore_tags(self): try: # typogrify should be able to ignore user specified tags, # but tries to be clever with widont extension - page = self.read_file(path='article.rst', TYPOGRIFY=True, - TYPOGRIFY_IGNORE_TAGS=['p']) - expected = ('THIS is some content. With some stuff to ' - '"typogrify"...
\nNow with added ' - 'support for ' - 'TLA.
\n') + page = self.read_file( + path="article.rst", TYPOGRIFY=True, TYPOGRIFY_IGNORE_TAGS=["p"] + ) + expected = ( + "THIS is some content. With some stuff to " + ""typogrify"...
\nNow with added " + 'support for ' + "TLA.
\n" + ) self.assertEqual(page.content, expected) # typogrify should ignore code blocks by default because # code blocks are composed inside the pre tag - page = self.read_file(path='article_with_code_block.rst', - TYPOGRIFY=True) + page = self.read_file(path="article_with_code_block.rst", TYPOGRIFY=True) - expected = ('An article with some code
\n' - ''
- 'x'
- ' &'
- ' y\nA block quote:
\n\nx ' - '& y\n' - '
Normal:\nx' - ' &' - ' y' - '
\n') + expected = ( + "An article with some code
\n" + ''
+ 'x'
+ ' &'
+ ' y\nA block quote:
\n\nx " + '& y\n' + "
Normal:\nx" + ' &' + " y" + "
\n" + ) self.assertEqual(page.content, expected) # instruct typogrify to also ignore blockquotes - page = self.read_file(path='article_with_code_block.rst', - TYPOGRIFY=True, - TYPOGRIFY_IGNORE_TAGS=['blockquote']) + page = self.read_file( + path="article_with_code_block.rst", + TYPOGRIFY=True, + TYPOGRIFY_IGNORE_TAGS=["blockquote"], + ) - expected = ('An article with some code
\n' - ''
- 'x'
- ' &'
- ' y\nA block quote:
\n\nx ' - '& y\n' - '
Normal:\nx' - ' &' - ' y' - '
\n') + expected = ( + "An article with some code
\n" + ''
+ 'x'
+ ' &'
+ ' y\nA block quote:
\n\nx " + "& y\n" + "
Normal:\nx" + ' &' + " y" + "
\n" + ) self.assertEqual(page.content, expected) except ImportError: - return unittest.skip('need the typogrify distribution') + return unittest.skip("need the typogrify distribution") except TypeError: - return unittest.skip('need typogrify version 2.0.4 or later') + return unittest.skip("need typogrify version 2.0.4 or later") def test_article_with_multiple_authors(self): - page = self.read_file(path='article_with_multiple_authors.rst') - expected = { - 'authors': ['First Author', 'Second Author'] - } + page = self.read_file(path="article_with_multiple_authors.rst") + expected = {"authors": ["First Author", "Second Author"]} self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_authors_semicolon(self): - page = self.read_file( - path='article_with_multiple_authors_semicolon.rst') - expected = { - 'authors': ['Author, First', 'Author, Second'] - } + page = self.read_file(path="article_with_multiple_authors_semicolon.rst") + expected = {"authors": ["Author, First", "Author, Second"]} self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_authors_list(self): - page = self.read_file(path='article_with_multiple_authors_list.rst') - expected = { - 'authors': ['Author, First', 'Author, Second'] - } + page = self.read_file(path="article_with_multiple_authors_list.rst") + expected = {"authors": ["Author, First", "Author, Second"]} self.assertDictHasSubset(page.metadata, expected) def test_default_date_formats(self): - tuple_date = self.read_file(path='article.rst', - DEFAULT_DATE=(2012, 5, 1)) - string_date = self.read_file(path='article.rst', - DEFAULT_DATE='2012-05-01') + tuple_date = self.read_file(path="article.rst", DEFAULT_DATE=(2012, 5, 1)) + string_date = self.read_file(path="article.rst", DEFAULT_DATE="2012-05-01") - self.assertEqual(tuple_date.metadata['date'], - string_date.metadata['date']) + self.assertEqual(tuple_date.metadata["date"], string_date.metadata["date"]) def test_parse_error(self): # Verify that it raises an Exception, not nothing and not SystemExit or # some such with self.assertRaisesRegex(Exception, "underline too short"): - self.read_file(path='../parse_error/parse_error.rst') + self.read_file(path="../parse_error/parse_error.rst") def test_typogrify_dashes_config(self): # Test default config page = self.read_file( - path='article_with_typogrify_dashes.rst', + path="article_with_typogrify_dashes.rst", TYPOGRIFY=True, - TYPOGRIFY_DASHES='default') + TYPOGRIFY_DASHES="default", + ) expected = "One: -; Two: —; Three: —-
\n" expected_title = "One -, two —, three —- dashes!" @@ -511,9 +508,10 @@ class RstReaderTest(ReaderTest): # Test 'oldschool' variant page = self.read_file( - path='article_with_typogrify_dashes.rst', + path="article_with_typogrify_dashes.rst", TYPOGRIFY=True, - TYPOGRIFY_DASHES='oldschool') + TYPOGRIFY_DASHES="oldschool", + ) expected = "One: -; Two: –; Three: —
\n" expected_title = "One -, two –, three — dashes!" @@ -522,9 +520,10 @@ class RstReaderTest(ReaderTest): # Test 'oldschool_inverted' variant page = self.read_file( - path='article_with_typogrify_dashes.rst', + path="article_with_typogrify_dashes.rst", TYPOGRIFY=True, - TYPOGRIFY_DASHES='oldschool_inverted') + TYPOGRIFY_DASHES="oldschool_inverted", + ) expected = "One: -; Two: —; Three: –
\n" expected_title = "One -, two —, three – dashes!" @@ -534,75 +533,73 @@ class RstReaderTest(ReaderTest): @unittest.skipUnless(readers.Markdown, "markdown isn't installed") class MdReaderTest(ReaderTest): - def test_article_with_metadata(self): reader = readers.MarkdownReader(settings=get_settings()) - content, metadata = reader.read( - _path('article_with_md_extension.md')) + content, metadata = reader.read(_path("article_with_md_extension.md")) expected = { - 'category': 'test', - 'title': 'Test md File', - 'summary': 'I have a lot to test
', - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'modified': SafeDatetime(2010, 12, 2, 10, 20), - 'tags': ['foo', 'bar', 'foobar'], + "category": "test", + "title": "Test md File", + "summary": "I have a lot to test
", + "date": SafeDatetime(2010, 12, 2, 10, 14), + "modified": SafeDatetime(2010, 12, 2, 10, 20), + "tags": ["foo", "bar", "foobar"], } self.assertDictHasSubset(metadata, expected) content, metadata = reader.read( - _path('article_with_markdown_and_nonascii_summary.md')) + _path("article_with_markdown_and_nonascii_summary.md") + ) expected = { - 'title': 'マックOS X 10.8でパイソンとVirtualenvをインストールと設定', - 'summary': 'パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
', - 'category': '指導書', - 'date': SafeDatetime(2012, 12, 20), - 'modified': SafeDatetime(2012, 12, 22), - 'tags': ['パイソン', 'マック'], - 'slug': 'python-virtualenv-on-mac-osx-mountain-lion-10.8', + "title": "マックOS X 10.8でパイソンとVirtualenvをインストールと設定", + "summary": "パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。
", + "category": "指導書", + "date": SafeDatetime(2012, 12, 20), + "modified": SafeDatetime(2012, 12, 22), + "tags": ["パイソン", "マック"], + "slug": "python-virtualenv-on-mac-osx-mountain-lion-10.8", } self.assertDictHasSubset(metadata, expected) def test_article_with_footnote(self): settings = get_settings() - ec = settings['MARKDOWN']['extension_configs'] - ec['markdown.extensions.footnotes'] = {'SEPARATOR': '-'} + ec = settings["MARKDOWN"]["extension_configs"] + ec["markdown.extensions.footnotes"] = {"SEPARATOR": "-"} reader = readers.MarkdownReader(settings) - content, metadata = reader.read( - _path('article_with_markdown_and_footnote.md')) + content, metadata = reader.read(_path("article_with_markdown_and_footnote.md")) expected_content = ( - 'This is some content' + "
This is some content" '1' - ' with some footnotes' + ">1" + " with some footnotes" '2
\n' - 'Summary with inline markup ' - 'should be supported.
'), - 'date': SafeDatetime(2012, 10, 31), - 'modified': SafeDatetime(2012, 11, 1), - 'multiline': [ - 'Line Metadata should be handle properly.', - 'See syntax of Meta-Data extension of ' - 'Python Markdown package:', - 'If a line is indented by 4 or more spaces,', - 'that line is assumed to be an additional line of the value', - 'for the previous keyword.', - 'A keyword may have as many lines as desired.', - ] + "title": "Article with markdown containing footnotes", + "summary": ( + "Summary with inline markup " + "should be supported.
" + ), + "date": SafeDatetime(2012, 10, 31), + "modified": SafeDatetime(2012, 11, 1), + "multiline": [ + "Line Metadata should be handle properly.", + "See syntax of Meta-Data extension of " "Python Markdown package:", + "If a line is indented by 4 or more spaces,", + "that line is assumed to be an additional line of the value", + "for the previous keyword.", + "A keyword may have as many lines as desired.", + ], } self.assertEqual(content, expected_content) self.assertDictHasSubset(metadata, expected_metadata) @@ -611,163 +608,173 @@ class MdReaderTest(ReaderTest): reader = readers.MarkdownReader(settings=get_settings()) # test to ensure the md file extension is being processed by the # correct reader - content, metadata = reader.read( - _path('article_with_md_extension.md')) + content, metadata = reader.read(_path("article_with_md_extension.md")) expected = ( "The quick brown fox jumped over the lazy dog's back.
") + "The quick brown fox jumped over the lazy dog's back.
" + ) self.assertEqual(content, expected) # test to ensure the mkd file extension is being processed by the # correct reader - content, metadata = reader.read( - _path('article_with_mkd_extension.mkd')) - expected = ("This is another markdown test file. Uses" - " the mkd extension.
") + content, metadata = reader.read(_path("article_with_mkd_extension.mkd")) + expected = ( + "This is another markdown test file. Uses" + " the mkd extension.
" + ) self.assertEqual(content, expected) # test to ensure the markdown file extension is being processed by the # correct reader content, metadata = reader.read( - _path('article_with_markdown_extension.markdown')) - expected = ("This is another markdown test file. Uses" - " the markdown extension.
") + _path("article_with_markdown_extension.markdown") + ) + expected = ( + "This is another markdown test file. Uses" + " the markdown extension.
" + ) self.assertEqual(content, expected) # test to ensure the mdown file extension is being processed by the # correct reader - content, metadata = reader.read( - _path('article_with_mdown_extension.mdown')) - expected = ("This is another markdown test file. Uses" - " the mdown extension.
") + content, metadata = reader.read(_path("article_with_mdown_extension.mdown")) + expected = ( + "This is another markdown test file. Uses" + " the mdown extension.
" + ) self.assertEqual(content, expected) def test_article_with_markdown_markup_extension(self): # test to ensure the markdown markup extension is being processed as # expected page = self.read_file( - path='article_with_markdown_markup_extensions.md', + path="article_with_markdown_markup_extensions.md", MARKDOWN={ - 'extension_configs': { - 'markdown.extensions.toc': {}, - 'markdown.extensions.codehilite': {}, - 'markdown.extensions.extra': {} + "extension_configs": { + "markdown.extensions.toc": {}, + "markdown.extensions.codehilite": {}, + "markdown.extensions.extra": {}, } - } + }, + ) + expected = ( + '\n" + 'Test: This metadata value looks like metadata
', + "title": "Article with markdown and nested summary metadata", + "summary": "Test: This metadata value looks like metadata
", } self.assertDictHasSubset(metadata, expected) def test_empty_file(self): reader = readers.MarkdownReader(settings=get_settings()) - content, metadata = reader.read( - _path('empty.md')) + content, metadata = reader.read(_path("empty.md")) self.assertEqual(metadata, {}) - self.assertEqual(content, '') + self.assertEqual(content, "") def test_empty_file_with_bom(self): reader = readers.MarkdownReader(settings=get_settings()) - content, metadata = reader.read( - _path('empty_with_bom.md')) + content, metadata = reader.read(_path("empty_with_bom.md")) self.assertEqual(metadata, {}) - self.assertEqual(content, '') + self.assertEqual(content, "") def test_typogrify_dashes_config(self): # Test default config page = self.read_file( - path='article_with_typogrify_dashes.md', + path="article_with_typogrify_dashes.md", TYPOGRIFY=True, - TYPOGRIFY_DASHES='default') + TYPOGRIFY_DASHES="default", + ) expected = "One: -; Two: —; Three: —-
" expected_title = "One -, two —, three —- dashes!" @@ -776,9 +783,10 @@ class MdReaderTest(ReaderTest): # Test 'oldschool' variant page = self.read_file( - path='article_with_typogrify_dashes.md', + path="article_with_typogrify_dashes.md", TYPOGRIFY=True, - TYPOGRIFY_DASHES='oldschool') + TYPOGRIFY_DASHES="oldschool", + ) expected = "One: -; Two: –; Three: —
" expected_title = "One -, two –, three — dashes!" @@ -787,9 +795,10 @@ class MdReaderTest(ReaderTest): # Test 'oldschool_inverted' variant page = self.read_file( - path='article_with_typogrify_dashes.md', + path="article_with_typogrify_dashes.md", TYPOGRIFY=True, - TYPOGRIFY_DASHES='oldschool_inverted') + TYPOGRIFY_DASHES="oldschool_inverted", + ) expected = "One: -; Two: —; Three: –
" expected_title = "One -, two —, three – dashes!" @@ -797,124 +806,130 @@ class MdReaderTest(ReaderTest): self.assertEqual(page.title, expected_title) def test_metadata_has_no_discarded_data(self): - md_filename = 'article_with_markdown_and_empty_tags.md' + md_filename = "article_with_markdown_and_empty_tags.md" - r = readers.Readers(cache_name='cache', settings=get_settings( - CACHE_CONTENT=True)) + r = readers.Readers( + cache_name="cache", settings=get_settings(CACHE_CONTENT=True) + ) page = r.read_file(base_path=CONTENT_PATH, path=md_filename) - __, cached_metadata = r.get_cached_data( - _path(md_filename), (None, None)) + __, cached_metadata = r.get_cached_data(_path(md_filename), (None, None)) - expected = { - 'title': 'Article with markdown and empty tags' - } + expected = {"title": "Article with markdown and empty tags"} self.assertEqual(cached_metadata, expected) - self.assertNotIn('tags', page.metadata) + self.assertNotIn("tags", page.metadata) self.assertDictHasSubset(page.metadata, expected) class HTMLReaderTest(ReaderTest): def test_article_with_comments(self): - page = self.read_file(path='article_with_comments.html') + page = self.read_file(path="article_with_comments.html") - self.assertEqual(''' + self.assertEqual( + """ Body content - ''', page.content) + """, + page.content, + ) def test_article_with_keywords(self): - page = self.read_file(path='article_with_keywords.html') + page = self.read_file(path="article_with_keywords.html") expected = { - 'tags': ['foo', 'bar', 'foobar'], + "tags": ["foo", "bar", "foobar"], } self.assertDictHasSubset(page.metadata, expected) def test_article_with_metadata(self): - page = self.read_file(path='article_with_metadata.html') + page = self.read_file(path="article_with_metadata.html") expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'This is a super article !', - 'summary': 'Summary and stuff', - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'tags': ['foo', 'bar', 'foobar'], - 'custom_field': 'http://notmyidea.org', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "This is a super article !", + "summary": "Summary and stuff", + "date": SafeDatetime(2010, 12, 2, 10, 14), + "tags": ["foo", "bar", "foobar"], + "custom_field": "http://notmyidea.org", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_similar_metadata_tags(self): - page = self.read_file(path='article_with_multiple_metadata_tags.html') + page = self.read_file(path="article_with_multiple_metadata_tags.html") expected = { - 'custom_field': ['https://getpelican.com', 'https://www.eff.org'], + "custom_field": ["https://getpelican.com", "https://www.eff.org"], } self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_authors(self): - page = self.read_file(path='article_with_multiple_authors.html') - expected = { - 'authors': ['First Author', 'Second Author'] - } + page = self.read_file(path="article_with_multiple_authors.html") + expected = {"authors": ["First Author", "Second Author"]} self.assertDictHasSubset(page.metadata, expected) def test_article_with_metadata_and_contents_attrib(self): - page = self.read_file(path='article_with_metadata_and_contents.html') + page = self.read_file(path="article_with_metadata_and_contents.html") expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'This is a super article !', - 'summary': 'Summary and stuff', - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'tags': ['foo', 'bar', 'foobar'], - 'custom_field': 'http://notmyidea.org', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "This is a super article !", + "summary": "Summary and stuff", + "date": SafeDatetime(2010, 12, 2, 10, 14), + "tags": ["foo", "bar", "foobar"], + "custom_field": "http://notmyidea.org", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_null_attributes(self): - page = self.read_file(path='article_with_null_attributes.html') + page = self.read_file(path="article_with_null_attributes.html") - self.assertEqual(''' + self.assertEqual( + """ Ensure that empty attributes are copied properly. - ''', page.content) + """, + page.content, + ) def test_article_with_attributes_containing_double_quotes(self): - page = self.read_file(path='article_with_attributes_containing_' + - 'double_quotes.html') - self.assertEqual(''' + page = self.read_file( + path="article_with_attributes_containing_" + "double_quotes.html" + ) + self.assertEqual( + """ Ensure that if an attribute value contains a double quote, it is surrounded with single quotes, otherwise with double quotes. Span content Span content Span content - ''', page.content) + """, + page.content, + ) def test_article_metadata_key_lowercase(self): # Keys of metadata should be lowercase. - page = self.read_file(path='article_with_uppercase_metadata.html') + page = self.read_file(path="article_with_uppercase_metadata.html") # Key should be lowercase - self.assertIn('category', page.metadata, 'Key should be lowercase.') + self.assertIn("category", page.metadata, "Key should be lowercase.") # Value should keep cases - self.assertEqual('Yeah', page.metadata.get('category')) + self.assertEqual("Yeah", page.metadata.get("category")) def test_article_with_nonconformant_meta_tags(self): - page = self.read_file(path='article_with_nonconformant_meta_tags.html') + page = self.read_file(path="article_with_nonconformant_meta_tags.html") expected = { - 'summary': 'Summary and stuff', - 'title': 'Article with Nonconformant HTML meta tags', + "summary": "Summary and stuff", + "title": "Article with Nonconformant HTML meta tags", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_inline_svg(self): - page = self.read_file(path='article_with_inline_svg.html') + page = self.read_file(path="article_with_inline_svg.html") expected = { - 'title': 'Article with an inline SVG', + "title": "Article with an inline SVG", } self.assertDictHasSubset(page.metadata, expected) diff --git a/pelican/tests/test_rstdirectives.py b/pelican/tests/test_rstdirectives.py index 6b733971..46ed6f49 100644 --- a/pelican/tests/test_rstdirectives.py +++ b/pelican/tests/test_rstdirectives.py @@ -6,11 +6,11 @@ from pelican.tests.support import unittest class Test_abbr_role(unittest.TestCase): def call_it(self, text): from pelican.rstdirectives import abbr_role + rawtext = text lineno = 42 - inliner = Mock(name='inliner') - nodes, system_messages = abbr_role( - 'abbr', rawtext, text, lineno, inliner) + inliner = Mock(name="inliner") + nodes, system_messages = abbr_role("abbr", rawtext, text, lineno, inliner) self.assertEqual(system_messages, []) self.assertEqual(len(nodes), 1) return nodes[0] @@ -18,14 +18,14 @@ class Test_abbr_role(unittest.TestCase): def test(self): node = self.call_it("Abbr (Abbreviation)") self.assertEqual(node.astext(), "Abbr") - self.assertEqual(node['explanation'], "Abbreviation") + self.assertEqual(node["explanation"], "Abbreviation") def test_newlines_in_explanation(self): node = self.call_it("CUL (See you\nlater)") self.assertEqual(node.astext(), "CUL") - self.assertEqual(node['explanation'], "See you\nlater") + self.assertEqual(node["explanation"], "See you\nlater") def test_newlines_in_abbr(self): node = self.call_it("US of\nA \n (USA)") self.assertEqual(node.astext(), "US of\nA") - self.assertEqual(node['explanation'], "USA") + self.assertEqual(node["explanation"], "USA") diff --git a/pelican/tests/test_server.py b/pelican/tests/test_server.py index 9af030f8..fd616ef7 100644 --- a/pelican/tests/test_server.py +++ b/pelican/tests/test_server.py @@ -17,10 +17,9 @@ class MockServer: class TestServer(unittest.TestCase): - def setUp(self): self.server = MockServer() - self.temp_output = mkdtemp(prefix='pelicantests.') + self.temp_output = mkdtemp(prefix="pelicantests.") self.old_cwd = os.getcwd() os.chdir(self.temp_output) @@ -29,32 +28,33 @@ class TestServer(unittest.TestCase): rmtree(self.temp_output) def test_get_path_that_exists(self): - handler = ComplexHTTPRequestHandler(MockRequest(), ('0.0.0.0', 8888), - self.server) + handler = ComplexHTTPRequestHandler( + MockRequest(), ("0.0.0.0", 8888), self.server + ) handler.base_path = self.temp_output - open(os.path.join(self.temp_output, 'foo.html'), 'a').close() - os.mkdir(os.path.join(self.temp_output, 'foo')) - open(os.path.join(self.temp_output, 'foo', 'index.html'), 'a').close() + open(os.path.join(self.temp_output, "foo.html"), "a").close() + os.mkdir(os.path.join(self.temp_output, "foo")) + open(os.path.join(self.temp_output, "foo", "index.html"), "a").close() - os.mkdir(os.path.join(self.temp_output, 'bar')) - open(os.path.join(self.temp_output, 'bar', 'index.html'), 'a').close() + os.mkdir(os.path.join(self.temp_output, "bar")) + open(os.path.join(self.temp_output, "bar", "index.html"), "a").close() - os.mkdir(os.path.join(self.temp_output, 'baz')) + os.mkdir(os.path.join(self.temp_output, "baz")) - for suffix in ['', '/']: + for suffix in ["", "/"]: # foo.html has precedence over foo/index.html - path = handler.get_path_that_exists('foo' + suffix) - self.assertEqual(path, 'foo.html') + path = handler.get_path_that_exists("foo" + suffix) + self.assertEqual(path, "foo.html") # folder with index.html should return folder/index.html - path = handler.get_path_that_exists('bar' + suffix) - self.assertEqual(path, 'bar/index.html') + path = handler.get_path_that_exists("bar" + suffix) + self.assertEqual(path, "bar/index.html") # folder without index.html should return same as input - path = handler.get_path_that_exists('baz' + suffix) - self.assertEqual(path, 'baz' + suffix) + path = handler.get_path_that_exists("baz" + suffix) + self.assertEqual(path, "baz" + suffix) # not existing path should return None - path = handler.get_path_that_exists('quux' + suffix) + path = handler.get_path_that_exists("quux" + suffix) self.assertIsNone(path) diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index 0f630ad5..0e77674d 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -4,10 +4,14 @@ import os from os.path import abspath, dirname, join -from pelican.settings import (DEFAULT_CONFIG, DEFAULT_THEME, - _printf_s_to_format_field, - configure_settings, - handle_deprecated_settings, read_settings) +from pelican.settings import ( + DEFAULT_CONFIG, + DEFAULT_THEME, + _printf_s_to_format_field, + configure_settings, + handle_deprecated_settings, + read_settings, +) from pelican.tests.support import unittest @@ -16,40 +20,39 @@ class TestSettingsConfiguration(unittest.TestCase): append new values to the settings (if any), and apply basic settings optimizations. """ + def setUp(self): self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") self.PATH = abspath(dirname(__file__)) - default_conf = join(self.PATH, 'default_conf.py') + default_conf = join(self.PATH, "default_conf.py") self.settings = read_settings(default_conf) def tearDown(self): locale.setlocale(locale.LC_ALL, self.old_locale) def test_overwrite_existing_settings(self): - self.assertEqual(self.settings.get('SITENAME'), "Alexis' log") - self.assertEqual( - self.settings.get('SITEURL'), - 'http://blog.notmyidea.org') + self.assertEqual(self.settings.get("SITENAME"), "Alexis' log") + self.assertEqual(self.settings.get("SITEURL"), "http://blog.notmyidea.org") def test_keep_default_settings(self): # Keep default settings if not defined. self.assertEqual( - self.settings.get('DEFAULT_CATEGORY'), - DEFAULT_CONFIG['DEFAULT_CATEGORY']) + self.settings.get("DEFAULT_CATEGORY"), DEFAULT_CONFIG["DEFAULT_CATEGORY"] + ) def test_dont_copy_small_keys(self): # Do not copy keys not in caps. - self.assertNotIn('foobar', self.settings) + self.assertNotIn("foobar", self.settings) def test_read_empty_settings(self): # Ensure an empty settings file results in default settings. settings = read_settings(None) expected = copy.deepcopy(DEFAULT_CONFIG) # Added by configure settings - expected['FEED_DOMAIN'] = '' - expected['ARTICLE_EXCLUDES'] = ['pages'] - expected['PAGE_EXCLUDES'] = [''] + expected["FEED_DOMAIN"] = "" + expected["ARTICLE_EXCLUDES"] = ["pages"] + expected["PAGE_EXCLUDES"] = [""] self.maxDiff = None self.assertDictEqual(settings, expected) @@ -57,250 +60,265 @@ class TestSettingsConfiguration(unittest.TestCase): # Make sure that the results from one settings call doesn't # effect past or future instances. self.PATH = abspath(dirname(__file__)) - default_conf = join(self.PATH, 'default_conf.py') + default_conf = join(self.PATH, "default_conf.py") settings = read_settings(default_conf) - settings['SITEURL'] = 'new-value' + settings["SITEURL"] = "new-value" new_settings = read_settings(default_conf) - self.assertNotEqual(new_settings['SITEURL'], settings['SITEURL']) + self.assertNotEqual(new_settings["SITEURL"], settings["SITEURL"]) def test_defaults_not_overwritten(self): # This assumes 'SITENAME': 'A Pelican Blog' settings = read_settings(None) - settings['SITENAME'] = 'Not a Pelican Blog' - self.assertNotEqual(settings['SITENAME'], DEFAULT_CONFIG['SITENAME']) + settings["SITENAME"] = "Not a Pelican Blog" + self.assertNotEqual(settings["SITENAME"], DEFAULT_CONFIG["SITENAME"]) def test_static_path_settings_safety(self): # Disallow static paths from being strings settings = { - 'STATIC_PATHS': 'foo/bar', - 'THEME_STATIC_PATHS': 'bar/baz', + "STATIC_PATHS": "foo/bar", + "THEME_STATIC_PATHS": "bar/baz", # These 4 settings are required to run configure_settings - 'PATH': '.', - 'THEME': DEFAULT_THEME, - 'SITEURL': 'http://blog.notmyidea.org/', - 'LOCALE': '', + "PATH": ".", + "THEME": DEFAULT_THEME, + "SITEURL": "http://blog.notmyidea.org/", + "LOCALE": "", } configure_settings(settings) + self.assertEqual(settings["STATIC_PATHS"], DEFAULT_CONFIG["STATIC_PATHS"]) self.assertEqual( - settings['STATIC_PATHS'], - DEFAULT_CONFIG['STATIC_PATHS']) - self.assertEqual( - settings['THEME_STATIC_PATHS'], - DEFAULT_CONFIG['THEME_STATIC_PATHS']) + settings["THEME_STATIC_PATHS"], DEFAULT_CONFIG["THEME_STATIC_PATHS"] + ) def test_configure_settings(self): # Manipulations to settings should be applied correctly. settings = { - 'SITEURL': 'http://blog.notmyidea.org/', - 'LOCALE': '', - 'PATH': os.curdir, - 'THEME': DEFAULT_THEME, + "SITEURL": "http://blog.notmyidea.org/", + "LOCALE": "", + "PATH": os.curdir, + "THEME": DEFAULT_THEME, } configure_settings(settings) # SITEURL should not have a trailing slash - self.assertEqual(settings['SITEURL'], 'http://blog.notmyidea.org') + self.assertEqual(settings["SITEURL"], "http://blog.notmyidea.org") # FEED_DOMAIN, if undefined, should default to SITEURL - self.assertEqual(settings['FEED_DOMAIN'], 'http://blog.notmyidea.org') + self.assertEqual(settings["FEED_DOMAIN"], "http://blog.notmyidea.org") - settings['FEED_DOMAIN'] = 'http://feeds.example.com' + settings["FEED_DOMAIN"] = "http://feeds.example.com" configure_settings(settings) - self.assertEqual(settings['FEED_DOMAIN'], 'http://feeds.example.com') + self.assertEqual(settings["FEED_DOMAIN"], "http://feeds.example.com") def test_theme_settings_exceptions(self): settings = self.settings # Check that theme lookup in "pelican/themes" functions as expected - settings['THEME'] = os.path.split(settings['THEME'])[1] + settings["THEME"] = os.path.split(settings["THEME"])[1] configure_settings(settings) - self.assertEqual(settings['THEME'], DEFAULT_THEME) + self.assertEqual(settings["THEME"], DEFAULT_THEME) # Check that non-existent theme raises exception - settings['THEME'] = 'foo' + settings["THEME"] = "foo" self.assertRaises(Exception, configure_settings, settings) def test_deprecated_dir_setting(self): settings = self.settings - settings['ARTICLE_DIR'] = 'foo' - settings['PAGE_DIR'] = 'bar' + settings["ARTICLE_DIR"] = "foo" + settings["PAGE_DIR"] = "bar" settings = handle_deprecated_settings(settings) - self.assertEqual(settings['ARTICLE_PATHS'], ['foo']) - self.assertEqual(settings['PAGE_PATHS'], ['bar']) + self.assertEqual(settings["ARTICLE_PATHS"], ["foo"]) + self.assertEqual(settings["PAGE_PATHS"], ["bar"]) with self.assertRaises(KeyError): - settings['ARTICLE_DIR'] - settings['PAGE_DIR'] + settings["ARTICLE_DIR"] + settings["PAGE_DIR"] def test_default_encoding(self): # Test that the user locale is set if not specified in settings - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") # empty string = user system locale - self.assertEqual(self.settings['LOCALE'], ['']) + self.assertEqual(self.settings["LOCALE"], [""]) configure_settings(self.settings) lc_time = locale.getlocale(locale.LC_TIME) # should be set to user locale # explicitly set locale to user pref and test - locale.setlocale(locale.LC_TIME, '') + locale.setlocale(locale.LC_TIME, "") self.assertEqual(lc_time, locale.getlocale(locale.LC_TIME)) def test_invalid_settings_throw_exception(self): # Test that the path name is valid # test that 'PATH' is set - settings = { - } + settings = {} self.assertRaises(Exception, configure_settings, settings) # Test that 'PATH' is valid - settings['PATH'] = '' + settings["PATH"] = "" self.assertRaises(Exception, configure_settings, settings) # Test nonexistent THEME - settings['PATH'] = os.curdir - settings['THEME'] = 'foo' + settings["PATH"] = os.curdir + settings["THEME"] = "foo" self.assertRaises(Exception, configure_settings, settings) def test__printf_s_to_format_field(self): - for s in ('%s', '{%s}', '{%s'): - option = 'foo/{}/bar.baz'.format(s) - result = _printf_s_to_format_field(option, 'slug') - expected = option % 'qux' - found = result.format(slug='qux') + for s in ("%s", "{%s}", "{%s"): + option = "foo/{}/bar.baz".format(s) + result = _printf_s_to_format_field(option, "slug") + expected = option % "qux" + found = result.format(slug="qux") self.assertEqual(expected, found) def test_deprecated_extra_templates_paths(self): settings = self.settings - settings['EXTRA_TEMPLATES_PATHS'] = ['/foo/bar', '/ha'] + settings["EXTRA_TEMPLATES_PATHS"] = ["/foo/bar", "/ha"] settings = handle_deprecated_settings(settings) - self.assertEqual(settings['THEME_TEMPLATES_OVERRIDES'], - ['/foo/bar', '/ha']) - self.assertNotIn('EXTRA_TEMPLATES_PATHS', settings) + self.assertEqual(settings["THEME_TEMPLATES_OVERRIDES"], ["/foo/bar", "/ha"]) + self.assertNotIn("EXTRA_TEMPLATES_PATHS", settings) def test_deprecated_paginated_direct_templates(self): settings = self.settings - settings['PAGINATED_DIRECT_TEMPLATES'] = ['index', 'archives'] - settings['PAGINATED_TEMPLATES'] = {'index': 10, 'category': None} + settings["PAGINATED_DIRECT_TEMPLATES"] = ["index", "archives"] + settings["PAGINATED_TEMPLATES"] = {"index": 10, "category": None} settings = handle_deprecated_settings(settings) - self.assertEqual(settings['PAGINATED_TEMPLATES'], - {'index': 10, 'category': None, 'archives': None}) - self.assertNotIn('PAGINATED_DIRECT_TEMPLATES', settings) + self.assertEqual( + settings["PAGINATED_TEMPLATES"], + {"index": 10, "category": None, "archives": None}, + ) + self.assertNotIn("PAGINATED_DIRECT_TEMPLATES", settings) def test_deprecated_paginated_direct_templates_from_file(self): # This is equivalent to reading a settings file that has # PAGINATED_DIRECT_TEMPLATES defined but no PAGINATED_TEMPLATES. - settings = read_settings(None, override={ - 'PAGINATED_DIRECT_TEMPLATES': ['index', 'archives'] - }) - self.assertEqual(settings['PAGINATED_TEMPLATES'], { - 'archives': None, - 'author': None, - 'index': None, - 'category': None, - 'tag': None}) - self.assertNotIn('PAGINATED_DIRECT_TEMPLATES', settings) + settings = read_settings( + None, override={"PAGINATED_DIRECT_TEMPLATES": ["index", "archives"]} + ) + self.assertEqual( + settings["PAGINATED_TEMPLATES"], + { + "archives": None, + "author": None, + "index": None, + "category": None, + "tag": None, + }, + ) + self.assertNotIn("PAGINATED_DIRECT_TEMPLATES", settings) def test_theme_and_extra_templates_exception(self): settings = self.settings - settings['EXTRA_TEMPLATES_PATHS'] = ['/ha'] - settings['THEME_TEMPLATES_OVERRIDES'] = ['/foo/bar'] + settings["EXTRA_TEMPLATES_PATHS"] = ["/ha"] + settings["THEME_TEMPLATES_OVERRIDES"] = ["/foo/bar"] self.assertRaises(Exception, handle_deprecated_settings, settings) def test_slug_and_slug_regex_substitutions_exception(self): settings = {} - settings['SLUG_REGEX_SUBSTITUTIONS'] = [('C++', 'cpp')] - settings['TAG_SUBSTITUTIONS'] = [('C#', 'csharp')] + settings["SLUG_REGEX_SUBSTITUTIONS"] = [("C++", "cpp")] + settings["TAG_SUBSTITUTIONS"] = [("C#", "csharp")] self.assertRaises(Exception, handle_deprecated_settings, settings) def test_deprecated_slug_substitutions(self): - default_slug_regex_subs = self.settings['SLUG_REGEX_SUBSTITUTIONS'] + default_slug_regex_subs = self.settings["SLUG_REGEX_SUBSTITUTIONS"] # If no deprecated setting is set, don't set new ones settings = {} settings = handle_deprecated_settings(settings) - self.assertNotIn('SLUG_REGEX_SUBSTITUTIONS', settings) - self.assertNotIn('TAG_REGEX_SUBSTITUTIONS', settings) - self.assertNotIn('CATEGORY_REGEX_SUBSTITUTIONS', settings) - self.assertNotIn('AUTHOR_REGEX_SUBSTITUTIONS', settings) + self.assertNotIn("SLUG_REGEX_SUBSTITUTIONS", settings) + self.assertNotIn("TAG_REGEX_SUBSTITUTIONS", settings) + self.assertNotIn("CATEGORY_REGEX_SUBSTITUTIONS", settings) + self.assertNotIn("AUTHOR_REGEX_SUBSTITUTIONS", settings) # If SLUG_SUBSTITUTIONS is set, set {SLUG, AUTHOR}_REGEX_SUBSTITUTIONS # correctly, don't set {CATEGORY, TAG}_REGEX_SUBSTITUTIONS settings = {} - settings['SLUG_SUBSTITUTIONS'] = [('C++', 'cpp')] + settings["SLUG_SUBSTITUTIONS"] = [("C++", "cpp")] settings = handle_deprecated_settings(settings) - self.assertEqual(settings.get('SLUG_REGEX_SUBSTITUTIONS'), - [(r'C\+\+', 'cpp')] + default_slug_regex_subs) - self.assertNotIn('TAG_REGEX_SUBSTITUTIONS', settings) - self.assertNotIn('CATEGORY_REGEX_SUBSTITUTIONS', settings) - self.assertEqual(settings.get('AUTHOR_REGEX_SUBSTITUTIONS'), - default_slug_regex_subs) + self.assertEqual( + settings.get("SLUG_REGEX_SUBSTITUTIONS"), + [(r"C\+\+", "cpp")] + default_slug_regex_subs, + ) + self.assertNotIn("TAG_REGEX_SUBSTITUTIONS", settings) + self.assertNotIn("CATEGORY_REGEX_SUBSTITUTIONS", settings) + self.assertEqual( + settings.get("AUTHOR_REGEX_SUBSTITUTIONS"), default_slug_regex_subs + ) # If {CATEGORY, TAG, AUTHOR}_SUBSTITUTIONS are set, set # {CATEGORY, TAG, AUTHOR}_REGEX_SUBSTITUTIONS correctly, don't set # SLUG_REGEX_SUBSTITUTIONS settings = {} - settings['TAG_SUBSTITUTIONS'] = [('C#', 'csharp')] - settings['CATEGORY_SUBSTITUTIONS'] = [('C#', 'csharp')] - settings['AUTHOR_SUBSTITUTIONS'] = [('Alexander Todorov', 'atodorov')] + settings["TAG_SUBSTITUTIONS"] = [("C#", "csharp")] + settings["CATEGORY_SUBSTITUTIONS"] = [("C#", "csharp")] + settings["AUTHOR_SUBSTITUTIONS"] = [("Alexander Todorov", "atodorov")] settings = handle_deprecated_settings(settings) - self.assertNotIn('SLUG_REGEX_SUBSTITUTIONS', settings) - self.assertEqual(settings['TAG_REGEX_SUBSTITUTIONS'], - [(r'C\#', 'csharp')] + default_slug_regex_subs) - self.assertEqual(settings['CATEGORY_REGEX_SUBSTITUTIONS'], - [(r'C\#', 'csharp')] + default_slug_regex_subs) - self.assertEqual(settings['AUTHOR_REGEX_SUBSTITUTIONS'], - [(r'Alexander\ Todorov', 'atodorov')] + - default_slug_regex_subs) + self.assertNotIn("SLUG_REGEX_SUBSTITUTIONS", settings) + self.assertEqual( + settings["TAG_REGEX_SUBSTITUTIONS"], + [(r"C\#", "csharp")] + default_slug_regex_subs, + ) + self.assertEqual( + settings["CATEGORY_REGEX_SUBSTITUTIONS"], + [(r"C\#", "csharp")] + default_slug_regex_subs, + ) + self.assertEqual( + settings["AUTHOR_REGEX_SUBSTITUTIONS"], + [(r"Alexander\ Todorov", "atodorov")] + default_slug_regex_subs, + ) # If {SLUG, CATEGORY, TAG, AUTHOR}_SUBSTITUTIONS are set, set # {SLUG, CATEGORY, TAG, AUTHOR}_REGEX_SUBSTITUTIONS correctly settings = {} - settings['SLUG_SUBSTITUTIONS'] = [('C++', 'cpp')] - settings['TAG_SUBSTITUTIONS'] = [('C#', 'csharp')] - settings['CATEGORY_SUBSTITUTIONS'] = [('C#', 'csharp')] - settings['AUTHOR_SUBSTITUTIONS'] = [('Alexander Todorov', 'atodorov')] + settings["SLUG_SUBSTITUTIONS"] = [("C++", "cpp")] + settings["TAG_SUBSTITUTIONS"] = [("C#", "csharp")] + settings["CATEGORY_SUBSTITUTIONS"] = [("C#", "csharp")] + settings["AUTHOR_SUBSTITUTIONS"] = [("Alexander Todorov", "atodorov")] settings = handle_deprecated_settings(settings) - self.assertEqual(settings['TAG_REGEX_SUBSTITUTIONS'], - [(r'C\+\+', 'cpp')] + [(r'C\#', 'csharp')] + - default_slug_regex_subs) - self.assertEqual(settings['CATEGORY_REGEX_SUBSTITUTIONS'], - [(r'C\+\+', 'cpp')] + [(r'C\#', 'csharp')] + - default_slug_regex_subs) - self.assertEqual(settings['AUTHOR_REGEX_SUBSTITUTIONS'], - [(r'Alexander\ Todorov', 'atodorov')] + - default_slug_regex_subs) + self.assertEqual( + settings["TAG_REGEX_SUBSTITUTIONS"], + [(r"C\+\+", "cpp")] + [(r"C\#", "csharp")] + default_slug_regex_subs, + ) + self.assertEqual( + settings["CATEGORY_REGEX_SUBSTITUTIONS"], + [(r"C\+\+", "cpp")] + [(r"C\#", "csharp")] + default_slug_regex_subs, + ) + self.assertEqual( + settings["AUTHOR_REGEX_SUBSTITUTIONS"], + [(r"Alexander\ Todorov", "atodorov")] + default_slug_regex_subs, + ) # Handle old 'skip' flags correctly settings = {} - settings['SLUG_SUBSTITUTIONS'] = [('C++', 'cpp', True)] - settings['AUTHOR_SUBSTITUTIONS'] = [('Alexander Todorov', 'atodorov', - False)] + settings["SLUG_SUBSTITUTIONS"] = [("C++", "cpp", True)] + settings["AUTHOR_SUBSTITUTIONS"] = [("Alexander Todorov", "atodorov", False)] settings = handle_deprecated_settings(settings) - self.assertEqual(settings.get('SLUG_REGEX_SUBSTITUTIONS'), - [(r'C\+\+', 'cpp')] + - [(r'(?u)\A\s*', ''), (r'(?u)\s*\Z', '')]) - self.assertEqual(settings['AUTHOR_REGEX_SUBSTITUTIONS'], - [(r'Alexander\ Todorov', 'atodorov')] + - default_slug_regex_subs) + self.assertEqual( + settings.get("SLUG_REGEX_SUBSTITUTIONS"), + [(r"C\+\+", "cpp")] + [(r"(?u)\A\s*", ""), (r"(?u)\s*\Z", "")], + ) + self.assertEqual( + settings["AUTHOR_REGEX_SUBSTITUTIONS"], + [(r"Alexander\ Todorov", "atodorov")] + default_slug_regex_subs, + ) def test_deprecated_slug_substitutions_from_file(self): # This is equivalent to reading a settings file that has # SLUG_SUBSTITUTIONS defined but no SLUG_REGEX_SUBSTITUTIONS. - settings = read_settings(None, override={ - 'SLUG_SUBSTITUTIONS': [('C++', 'cpp')] - }) - self.assertEqual(settings['SLUG_REGEX_SUBSTITUTIONS'], - [(r'C\+\+', 'cpp')] + - self.settings['SLUG_REGEX_SUBSTITUTIONS']) - self.assertNotIn('SLUG_SUBSTITUTIONS', settings) + settings = read_settings( + None, override={"SLUG_SUBSTITUTIONS": [("C++", "cpp")]} + ) + self.assertEqual( + settings["SLUG_REGEX_SUBSTITUTIONS"], + [(r"C\+\+", "cpp")] + self.settings["SLUG_REGEX_SUBSTITUTIONS"], + ) + self.assertNotIn("SLUG_SUBSTITUTIONS", settings) diff --git a/pelican/tests/test_testsuite.py b/pelican/tests/test_testsuite.py index fa930139..a9a0c200 100644 --- a/pelican/tests/test_testsuite.py +++ b/pelican/tests/test_testsuite.py @@ -4,7 +4,6 @@ from pelican.tests.support import unittest class TestSuiteTest(unittest.TestCase): - def test_error_on_warning(self): with self.assertRaises(UserWarning): - warnings.warn('test warning') + warnings.warn("test warning") diff --git a/pelican/tests/test_urlwrappers.py b/pelican/tests/test_urlwrappers.py index 66ae1524..13632e3a 100644 --- a/pelican/tests/test_urlwrappers.py +++ b/pelican/tests/test_urlwrappers.py @@ -5,22 +5,22 @@ from pelican.urlwrappers import Author, Category, Tag, URLWrapper class TestURLWrapper(unittest.TestCase): def test_ordering(self): # URLWrappers are sorted by name - wrapper_a = URLWrapper(name='first', settings={}) - wrapper_b = URLWrapper(name='last', settings={}) + wrapper_a = URLWrapper(name="first", settings={}) + wrapper_b = URLWrapper(name="last", settings={}) self.assertFalse(wrapper_a > wrapper_b) self.assertFalse(wrapper_a >= wrapper_b) self.assertFalse(wrapper_a == wrapper_b) self.assertTrue(wrapper_a != wrapper_b) self.assertTrue(wrapper_a <= wrapper_b) self.assertTrue(wrapper_a < wrapper_b) - wrapper_b.name = 'first' + wrapper_b.name = "first" self.assertFalse(wrapper_a > wrapper_b) self.assertTrue(wrapper_a >= wrapper_b) self.assertTrue(wrapper_a == wrapper_b) self.assertFalse(wrapper_a != wrapper_b) self.assertTrue(wrapper_a <= wrapper_b) self.assertFalse(wrapper_a < wrapper_b) - wrapper_a.name = 'last' + wrapper_a.name = "last" self.assertTrue(wrapper_a > wrapper_b) self.assertTrue(wrapper_a >= wrapper_b) self.assertFalse(wrapper_a == wrapper_b) @@ -29,57 +29,68 @@ class TestURLWrapper(unittest.TestCase): self.assertFalse(wrapper_a < wrapper_b) def test_equality(self): - tag = Tag('test', settings={}) - cat = Category('test', settings={}) - author = Author('test', settings={}) + tag = Tag("test", settings={}) + cat = Category("test", settings={}) + author = Author("test", settings={}) # same name, but different class self.assertNotEqual(tag, cat) self.assertNotEqual(tag, author) # should be equal vs text representing the same name - self.assertEqual(tag, 'test') + self.assertEqual(tag, "test") # should not be equal vs binary - self.assertNotEqual(tag, b'test') + self.assertNotEqual(tag, b"test") # Tags describing the same should be equal - tag_equal = Tag('Test', settings={}) + tag_equal = Tag("Test", settings={}) self.assertEqual(tag, tag_equal) # Author describing the same should be equal - author_equal = Author('Test', settings={}) + author_equal = Author("Test", settings={}) self.assertEqual(author, author_equal) - cat_ascii = Category('指導書', settings={}) - self.assertEqual(cat_ascii, 'zhi dao shu') + cat_ascii = Category("指導書", settings={}) + self.assertEqual(cat_ascii, "zhi dao shu") def test_slugify_with_substitutions_and_dots(self): - tag = Tag('Tag Dot', settings={'TAG_REGEX_SUBSTITUTIONS': [ - ('Tag Dot', 'tag.dot'), - ]}) - cat = Category('Category Dot', - settings={'CATEGORY_REGEX_SUBSTITUTIONS': [ - ('Category Dot', 'cat.dot'), - ]}) + tag = Tag( + "Tag Dot", + settings={ + "TAG_REGEX_SUBSTITUTIONS": [ + ("Tag Dot", "tag.dot"), + ] + }, + ) + cat = Category( + "Category Dot", + settings={ + "CATEGORY_REGEX_SUBSTITUTIONS": [ + ("Category Dot", "cat.dot"), + ] + }, + ) - self.assertEqual(tag.slug, 'tag.dot') - self.assertEqual(cat.slug, 'cat.dot') + self.assertEqual(tag.slug, "tag.dot") + self.assertEqual(cat.slug, "cat.dot") def test_author_slug_substitutions(self): - settings = {'AUTHOR_REGEX_SUBSTITUTIONS': [ - ('Alexander Todorov', 'atodorov'), - ('Krasimir Tsonev', 'krasimir'), - (r'[^\w\s-]', ''), - (r'(?u)\A\s*', ''), - (r'(?u)\s*\Z', ''), - (r'[-\s]+', '-'), - ]} + settings = { + "AUTHOR_REGEX_SUBSTITUTIONS": [ + ("Alexander Todorov", "atodorov"), + ("Krasimir Tsonev", "krasimir"), + (r"[^\w\s-]", ""), + (r"(?u)\A\s*", ""), + (r"(?u)\s*\Z", ""), + (r"[-\s]+", "-"), + ] + } - author1 = Author('Mr. Senko', settings=settings) - author2 = Author('Alexander Todorov', settings=settings) - author3 = Author('Krasimir Tsonev', settings=settings) + author1 = Author("Mr. Senko", settings=settings) + author2 = Author("Alexander Todorov", settings=settings) + author3 = Author("Krasimir Tsonev", settings=settings) - self.assertEqual(author1.slug, 'mr-senko') - self.assertEqual(author2.slug, 'atodorov') - self.assertEqual(author3.slug, 'krasimir') + self.assertEqual(author1.slug, "mr-senko") + self.assertEqual(author2.slug, "atodorov") + self.assertEqual(author3.slug, "krasimir") diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 40aff005..22dd8e38 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -14,25 +14,29 @@ except ModuleNotFoundError: from pelican import utils from pelican.generators import TemplatePagesGenerator from pelican.settings import read_settings -from pelican.tests.support import (LoggedTestCase, get_article, - locale_available, unittest) +from pelican.tests.support import ( + LoggedTestCase, + get_article, + locale_available, + unittest, +) from pelican.writers import Writer class TestUtils(LoggedTestCase): - _new_attribute = 'new_value' + _new_attribute = "new_value" def setUp(self): super().setUp() - self.temp_output = mkdtemp(prefix='pelicantests.') + self.temp_output = mkdtemp(prefix="pelicantests.") def tearDown(self): super().tearDown() shutil.rmtree(self.temp_output) @utils.deprecated_attribute( - old='_old_attribute', new='_new_attribute', - since=(3, 1, 0), remove=(4, 1, 3)) + old="_old_attribute", new="_new_attribute", since=(3, 1, 0), remove=(4, 1, 3) + ) def _old_attribute(): return None @@ -41,69 +45,109 @@ class TestUtils(LoggedTestCase): self.assertEqual(value, self._new_attribute) self.assertLogCountEqual( count=1, - msg=('_old_attribute has been deprecated since 3.1.0 and will be ' - 'removed by version 4.1.3. Use _new_attribute instead'), - level=logging.WARNING) + msg=( + "_old_attribute has been deprecated since 3.1.0 and will be " + "removed by version 4.1.3. Use _new_attribute instead" + ), + level=logging.WARNING, + ) def test_get_date(self): # valid ones date = utils.SafeDatetime(year=2012, month=11, day=22) - date_hour = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11) + date_hour = utils.SafeDatetime(year=2012, month=11, day=22, hour=22, minute=11) date_hour_z = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, - tzinfo=timezone.utc) + year=2012, month=11, day=22, hour=22, minute=11, tzinfo=timezone.utc + ) date_hour_est = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, - tzinfo=ZoneInfo("EST")) + year=2012, month=11, day=22, hour=22, minute=11, tzinfo=ZoneInfo("EST") + ) date_hour_sec = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, second=10) + year=2012, month=11, day=22, hour=22, minute=11, second=10 + ) date_hour_sec_z = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, second=10, - tzinfo=timezone.utc) + year=2012, + month=11, + day=22, + hour=22, + minute=11, + second=10, + tzinfo=timezone.utc, + ) date_hour_sec_est = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, second=10, - tzinfo=ZoneInfo("EST")) + year=2012, + month=11, + day=22, + hour=22, + minute=11, + second=10, + tzinfo=ZoneInfo("EST"), + ) date_hour_sec_frac_z = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, second=10, - microsecond=123000, tzinfo=timezone.utc) + year=2012, + month=11, + day=22, + hour=22, + minute=11, + second=10, + microsecond=123000, + tzinfo=timezone.utc, + ) dates = { - '2012-11-22': date, - '2012/11/22': date, - '2012-11-22 22:11': date_hour, - '2012/11/22 22:11': date_hour, - '22-11-2012': date, - '22/11/2012': date, - '22.11.2012': date, - '22.11.2012 22:11': date_hour, - '2012-11-22T22:11Z': date_hour_z, - '2012-11-22T22:11-0500': date_hour_est, - '2012-11-22 22:11:10': date_hour_sec, - '2012-11-22T22:11:10Z': date_hour_sec_z, - '2012-11-22T22:11:10-0500': date_hour_sec_est, - '2012-11-22T22:11:10.123Z': date_hour_sec_frac_z, + "2012-11-22": date, + "2012/11/22": date, + "2012-11-22 22:11": date_hour, + "2012/11/22 22:11": date_hour, + "22-11-2012": date, + "22/11/2012": date, + "22.11.2012": date, + "22.11.2012 22:11": date_hour, + "2012-11-22T22:11Z": date_hour_z, + "2012-11-22T22:11-0500": date_hour_est, + "2012-11-22 22:11:10": date_hour_sec, + "2012-11-22T22:11:10Z": date_hour_sec_z, + "2012-11-22T22:11:10-0500": date_hour_sec_est, + "2012-11-22T22:11:10.123Z": date_hour_sec_frac_z, } # examples from http://www.w3.org/TR/NOTE-datetime iso_8601_date = utils.SafeDatetime(year=1997, month=7, day=16) iso_8601_date_hour_tz = utils.SafeDatetime( - year=1997, month=7, day=16, hour=19, minute=20, - tzinfo=ZoneInfo("Europe/London")) + year=1997, + month=7, + day=16, + hour=19, + minute=20, + tzinfo=ZoneInfo("Europe/London"), + ) iso_8601_date_hour_sec_tz = utils.SafeDatetime( - year=1997, month=7, day=16, hour=19, minute=20, second=30, - tzinfo=ZoneInfo("Europe/London")) + year=1997, + month=7, + day=16, + hour=19, + minute=20, + second=30, + tzinfo=ZoneInfo("Europe/London"), + ) iso_8601_date_hour_sec_ms_tz = utils.SafeDatetime( - year=1997, month=7, day=16, hour=19, minute=20, second=30, - microsecond=450000, tzinfo=ZoneInfo("Europe/London")) + year=1997, + month=7, + day=16, + hour=19, + minute=20, + second=30, + microsecond=450000, + tzinfo=ZoneInfo("Europe/London"), + ) iso_8601 = { - '1997-07-16': iso_8601_date, - '1997-07-16T19:20+01:00': iso_8601_date_hour_tz, - '1997-07-16T19:20:30+01:00': iso_8601_date_hour_sec_tz, - '1997-07-16T19:20:30.45+01:00': iso_8601_date_hour_sec_ms_tz, + "1997-07-16": iso_8601_date, + "1997-07-16T19:20+01:00": iso_8601_date_hour_tz, + "1997-07-16T19:20:30+01:00": iso_8601_date_hour_sec_tz, + "1997-07-16T19:20:30.45+01:00": iso_8601_date_hour_sec_ms_tz, } # invalid ones - invalid_dates = ['2010-110-12', 'yay'] + invalid_dates = ["2010-110-12", "yay"] for value, expected in dates.items(): self.assertEqual(utils.get_date(value), expected, value) @@ -115,219 +159,247 @@ class TestUtils(LoggedTestCase): self.assertRaises(ValueError, utils.get_date, item) def test_slugify(self): - - samples = (('this is a test', 'this-is-a-test'), - ('this is a test', 'this-is-a-test'), - ('this → is ← a ↑ test', 'this-is-a-test'), - ('this--is---a test', 'this-is-a-test'), - ('unicode測試許功蓋,你看到了嗎?', - 'unicodece-shi-xu-gong-gai-ni-kan-dao-liao-ma'), - ('大飯原発4号機、18日夜起動へ', - 'da-fan-yuan-fa-4hao-ji-18ri-ye-qi-dong-he'),) + samples = ( + ("this is a test", "this-is-a-test"), + ("this is a test", "this-is-a-test"), + ("this → is ← a ↑ test", "this-is-a-test"), + ("this--is---a test", "this-is-a-test"), + ( + "unicode測試許功蓋,你看到了嗎?", + "unicodece-shi-xu-gong-gai-ni-kan-dao-liao-ma", + ), + ( + "大飯原発4号機、18日夜起動へ", + "da-fan-yuan-fa-4hao-ji-18ri-ye-qi-dong-he", + ), + ) settings = read_settings() - subs = settings['SLUG_REGEX_SUBSTITUTIONS'] + subs = settings["SLUG_REGEX_SUBSTITUTIONS"] for value, expected in samples: self.assertEqual(utils.slugify(value, regex_subs=subs), expected) - self.assertEqual(utils.slugify('Cat', regex_subs=subs), 'cat') + self.assertEqual(utils.slugify("Cat", regex_subs=subs), "cat") self.assertEqual( - utils.slugify('Cat', regex_subs=subs, preserve_case=False), 'cat') + utils.slugify("Cat", regex_subs=subs, preserve_case=False), "cat" + ) self.assertEqual( - utils.slugify('Cat', regex_subs=subs, preserve_case=True), 'Cat') + utils.slugify("Cat", regex_subs=subs, preserve_case=True), "Cat" + ) def test_slugify_use_unicode(self): - samples = ( - ('this is a test', 'this-is-a-test'), - ('this is a test', 'this-is-a-test'), - ('this → is ← a ↑ test', 'this-is-a-test'), - ('this--is---a test', 'this-is-a-test'), - ('unicode測試許功蓋,你看到了嗎?', 'unicode測試許功蓋你看到了嗎'), - ('Çığ', 'çığ') + ("this is a test", "this-is-a-test"), + ("this is a test", "this-is-a-test"), + ("this → is ← a ↑ test", "this-is-a-test"), + ("this--is---a test", "this-is-a-test"), + ("unicode測試許功蓋,你看到了嗎?", "unicode測試許功蓋你看到了嗎"), + ("Çığ", "çığ"), ) settings = read_settings() - subs = settings['SLUG_REGEX_SUBSTITUTIONS'] + subs = settings["SLUG_REGEX_SUBSTITUTIONS"] for value, expected in samples: self.assertEqual( - utils.slugify(value, regex_subs=subs, use_unicode=True), - expected) + utils.slugify(value, regex_subs=subs, use_unicode=True), expected + ) # check with preserve case for value, expected in samples: self.assertEqual( - utils.slugify('Çığ', regex_subs=subs, - preserve_case=True, use_unicode=True), - 'Çığ') + utils.slugify( + "Çığ", regex_subs=subs, preserve_case=True, use_unicode=True + ), + "Çığ", + ) # check normalization samples = ( - ('大飯原発4号機、18日夜起動へ', '大飯原発4号機18日夜起動へ'), + ("大飯原発4号機、18日夜起動へ", "大飯原発4号機18日夜起動へ"), ( - '\N{LATIN SMALL LETTER C}\N{COMBINING CEDILLA}', - '\N{LATIN SMALL LETTER C WITH CEDILLA}' - ) + "\N{LATIN SMALL LETTER C}\N{COMBINING CEDILLA}", + "\N{LATIN SMALL LETTER C WITH CEDILLA}", + ), ) for value, expected in samples: self.assertEqual( - utils.slugify(value, regex_subs=subs, use_unicode=True), - expected) + utils.slugify(value, regex_subs=subs, use_unicode=True), expected + ) def test_slugify_substitute(self): - - samples = (('C++ is based on C', 'cpp-is-based-on-c'), - ('C+++ test C+ test', 'cpp-test-c-test'), - ('c++, c#, C#, C++', 'cpp-c-sharp-c-sharp-cpp'), - ('c++-streams', 'cpp-streams'),) + samples = ( + ("C++ is based on C", "cpp-is-based-on-c"), + ("C+++ test C+ test", "cpp-test-c-test"), + ("c++, c#, C#, C++", "cpp-c-sharp-c-sharp-cpp"), + ("c++-streams", "cpp-streams"), + ) settings = read_settings() subs = [ - (r'C\+\+', 'CPP'), - (r'C#', 'C-SHARP'), - ] + settings['SLUG_REGEX_SUBSTITUTIONS'] + (r"C\+\+", "CPP"), + (r"C#", "C-SHARP"), + ] + settings["SLUG_REGEX_SUBSTITUTIONS"] for value, expected in samples: self.assertEqual(utils.slugify(value, regex_subs=subs), expected) def test_slugify_substitute_and_keeping_non_alphanum(self): - - samples = (('Fedora QA', 'fedora.qa'), - ('C++ is used by Fedora QA', 'cpp is used by fedora.qa'), - ('C++ is based on C', 'cpp is based on c'), - ('C+++ test C+ test', 'cpp+ test c+ test'),) + samples = ( + ("Fedora QA", "fedora.qa"), + ("C++ is used by Fedora QA", "cpp is used by fedora.qa"), + ("C++ is based on C", "cpp is based on c"), + ("C+++ test C+ test", "cpp+ test c+ test"), + ) subs = [ - (r'Fedora QA', 'fedora.qa'), - (r'c\+\+', 'cpp'), + (r"Fedora QA", "fedora.qa"), + (r"c\+\+", "cpp"), ] for value, expected in samples: self.assertEqual(utils.slugify(value, regex_subs=subs), expected) def test_get_relative_path(self): - - samples = ((os.path.join('test', 'test.html'), os.pardir), - (os.path.join('test', 'test', 'test.html'), - os.path.join(os.pardir, os.pardir)), - ('test.html', os.curdir), - (os.path.join('/test', 'test.html'), os.pardir), - (os.path.join('/test', 'test', 'test.html'), - os.path.join(os.pardir, os.pardir)), - ('/test.html', os.curdir),) + samples = ( + (os.path.join("test", "test.html"), os.pardir), + ( + os.path.join("test", "test", "test.html"), + os.path.join(os.pardir, os.pardir), + ), + ("test.html", os.curdir), + (os.path.join("/test", "test.html"), os.pardir), + ( + os.path.join("/test", "test", "test.html"), + os.path.join(os.pardir, os.pardir), + ), + ("/test.html", os.curdir), + ) for value, expected in samples: self.assertEqual(utils.get_relative_path(value), expected) def test_truncate_html_words(self): # Plain text. + self.assertEqual(utils.truncate_html_words("short string", 20), "short string") self.assertEqual( - utils.truncate_html_words('short string', 20), - 'short string') - self.assertEqual( - utils.truncate_html_words('word ' * 100, 20), - 'word ' * 20 + '…') + utils.truncate_html_words("word " * 100, 20), "word " * 20 + "…" + ) # Plain text with Unicode content. self.assertEqual( utils.truncate_html_words( - '我愿意这样,朋友——我独自远行,不但没有你,\ - 并且再没有别的影在黑暗里。', 12 + "我愿意这样,朋友——我独自远行,不但没有你,\ + 并且再没有别的影在黑暗里。", + 12, ), - '我愿意这样,朋友——我独自远行' + ' …') + "我愿意这样,朋友——我独自远行" + " …", + ) self.assertEqual( utils.truncate_html_words( - 'Ты мелькнула, ты предстала, Снова сердце задрожало,', 3 + "Ты мелькнула, ты предстала, Снова сердце задрожало,", 3 ), - 'Ты мелькнула, ты' + ' …') + "Ты мелькнула, ты" + " …", + ) self.assertEqual( - utils.truncate_html_words( - 'Trong đầm gì đẹp bằng sen', 4 - ), - 'Trong đầm gì đẹp' + ' …') + utils.truncate_html_words("Trong đầm gì đẹp bằng sen", 4), + "Trong đầm gì đẹp" + " …", + ) # Words enclosed or intervaled by HTML tags. self.assertEqual( - utils.truncate_html_words('' + 'word ' * 100 + '
', 20), - '' + 'word ' * 20 + '…
') + utils.truncate_html_words("" + "word " * 100 + "
", 20), + "" + "word " * 20 + "…
", + ) self.assertEqual( utils.truncate_html_words( - '' + 'word ' * 100 + '', 20), - '' + 'word ' * 20 + '…') + '' + "word " * 100 + "", 20 + ), + '' + "word " * 20 + "…", + ) self.assertEqual( - utils.truncate_html_words('' + 'word ' * 100 + '
', 20, - 'marker'), - '' + 'word ' * 20 + 'marker
') + utils.truncate_html_words( + "" + "word " * 100 + "
", 20, "marker" + ), + "" + "word " * 20 + "marker
", + ) self.assertEqual( utils.truncate_html_words( - '' + 'word ' * 100 + '', 20, - 'marker'), - '' + 'word ' * 20 + 'marker') + '' + "word " * 100 + "", + 20, + "marker", + ), + '' + "word " * 20 + "marker", + ) self.assertEqual( - utils.truncate_html_words('