summaryrefslogtreecommitdiffstats
path: root/nikola/plugins/command
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/plugins/command')
-rw-r--r--nikola/plugins/command/__init__.py2
-rw-r--r--nikola/plugins/command/auto/__init__.py46
-rw-r--r--nikola/plugins/command/check.py42
-rw-r--r--nikola/plugins/command/console.py2
-rw-r--r--nikola/plugins/command/default_config.py9
-rw-r--r--nikola/plugins/command/deploy.py2
-rw-r--r--nikola/plugins/command/github_deploy.py2
-rw-r--r--nikola/plugins/command/import_wordpress.py9
-rw-r--r--nikola/plugins/command/init.py2
-rw-r--r--nikola/plugins/command/new_page.py4
-rw-r--r--nikola/plugins/command/new_post.py8
-rw-r--r--nikola/plugins/command/orphans.py2
-rw-r--r--nikola/plugins/command/plugin.py2
-rw-r--r--nikola/plugins/command/rst2html/__init__.py4
-rw-r--r--nikola/plugins/command/serve.py2
-rw-r--r--nikola/plugins/command/status.py2
-rw-r--r--nikola/plugins/command/subtheme.py2
-rw-r--r--nikola/plugins/command/theme.py2
-rw-r--r--nikola/plugins/command/version.py2
19 files changed, 91 insertions, 55 deletions
diff --git a/nikola/plugins/command/__init__.py b/nikola/plugins/command/__init__.py
index 6c8e81a..e5cd68a 100644
--- a/nikola/plugins/command/__init__.py
+++ b/nikola/plugins/command/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/auto/__init__.py b/nikola/plugins/command/auto/__init__.py
index b13b645..d272c23 100644
--- a/nikola/plugins/command/auto/__init__.py
+++ b/nikola/plugins/command/auto/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Chris Warrick, Roberto Alsina and others.
+# Copyright © 2012-2024 Chris Warrick, Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -35,6 +35,7 @@ import stat
import subprocess
import sys
import typing
+import urllib.parse
import webbrowser
import blinker
@@ -67,6 +68,17 @@ if sys.platform == 'win32':
asyncio.set_event_loop(asyncio.ProactorEventLoop())
+def base_path_from_siteuri(siteuri: str) -> str:
+ """Extract the path part from a URI such as site['SITE_URL'].
+
+ The path never ends with a "/". (If only "/" is intended, it is empty.)
+ """
+ path = urllib.parse.urlsplit(siteuri).path
+ if path.endswith("/"):
+ path = path[:-1]
+ return path
+
+
class CommandAuto(Command):
"""Automatic rebuilds for Nikola."""
@@ -239,8 +251,10 @@ class CommandAuto(Command):
# Server can be disabled (Issue #1883)
self.has_server = not options['no-server']
+ base_path = base_path_from_siteuri(self.site.config['SITE_URL'])
+
if self.has_server:
- loop.run_until_complete(self.set_up_server(host, port, out_folder))
+ loop.run_until_complete(self.set_up_server(host, port, base_path, out_folder))
# Run an initial build so we are up-to-date. The server is running, but we are not watching yet.
loop.run_until_complete(self.run_initial_rebuild())
@@ -293,9 +307,12 @@ class CommandAuto(Command):
if browser:
# Some browsers fail to load 0.0.0.0 (Issue #2755)
if host == '0.0.0.0':
- server_url = "http://127.0.0.1:{0}/".format(port)
- self.logger.info("Opening {0} in the default web browser...".format(server_url))
- webbrowser.open(server_url)
+ browser_url = "http://127.0.0.1:{0}/{1}".format(port, base_path.lstrip("/"))
+ else:
+ # server_url always ends with a "/":
+ browser_url = "{0}{1}".format(server_url, base_path.lstrip("/"))
+ self.logger.info("Opening {0} in the default web browser...".format(browser_url))
+ webbrowser.open(browser_url)
# Run the event loop forever and handle shutdowns.
try:
@@ -320,13 +337,13 @@ class CommandAuto(Command):
self.wd_observer.join()
loop.close()
- async def set_up_server(self, host: str, port: int, out_folder: str) -> None:
+ async def set_up_server(self, host: str, port: int, base_path: str, out_folder: str) -> None:
"""Set up aiohttp server and start it."""
webapp = web.Application()
webapp.router.add_get('/livereload.js', self.serve_livereload_js)
webapp.router.add_get('/robots.txt', self.serve_robots_txt)
webapp.router.add_route('*', '/livereload', self.websocket_handler)
- resource = IndexHtmlStaticResource(True, self.snippet, '', out_folder)
+ resource = IndexHtmlStaticResource(True, self.snippet, base_path, out_folder)
webapp.router.register_resource(resource)
webapp.on_shutdown.append(self.remove_websockets)
@@ -587,13 +604,11 @@ class NikolaEventHandler:
self.function = function
self.loop = loop
- async def on_any_event(self, event):
- """Handle all file events."""
- await self.function(event)
-
def dispatch(self, event):
"""Dispatch events to handler."""
- self.loop.call_soon_threadsafe(asyncio.ensure_future, self.on_any_event(event))
+ if event.event_type in {"opened", "closed"}:
+ return
+ self.loop.call_soon_threadsafe(asyncio.ensure_future, self.function(event))
class ConfigEventHandler(NikolaEventHandler):
@@ -601,11 +616,10 @@ class ConfigEventHandler(NikolaEventHandler):
def __init__(self, configuration_filename, function, loop):
"""Initialize the handler."""
+ super().__init__(function, loop)
self.configuration_filename = configuration_filename
- self.function = function
- self.loop = loop
- async def on_any_event(self, event):
+ def dispatch(self, event):
"""Handle file events if they concern the configuration file."""
if event._src_path == self.configuration_filename:
- await self.function(event)
+ super().dispatch(event)
diff --git a/nikola/plugins/command/check.py b/nikola/plugins/command/check.py
index f9b701b..5bcbced 100644
--- a/nikola/plugins/command/check.py
+++ b/nikola/plugins/command/check.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -148,6 +148,22 @@ class CommandCheck(Command):
'default': False,
'help': 'Check that remote links work.',
},
+ {
+ 'name': 'timeout',
+ 'long': 'timeout',
+ 'short': 't',
+ 'type': int,
+ 'default': 30,
+ 'help': 'Timeout (in seconds) for HTTP requests in remote checks.',
+ },
+ {
+ 'name': 'ignore_query_strings',
+ 'long': 'ignore-query-strings',
+ 'short': 'q',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Ignore query strings for internal links.',
+ }
]
def _execute(self, options, args):
@@ -160,8 +176,9 @@ class CommandCheck(Command):
else:
self.logger.level = logging.WARNING
failure = False
+ self.timeout = options['timeout']
if options['links']:
- failure |= self.scan_links(options['find_sources'], options['remote'])
+ failure |= self.scan_links(options['find_sources'], options['remote'], options['ignore_query_strings'])
if options['files']:
failure |= self.scan_files()
if options['clean']:
@@ -171,9 +188,10 @@ class CommandCheck(Command):
existing_targets = set([])
checked_remote_targets = {}
+ timeout = None
cache = {}
- def analyze(self, fname, find_sources=False, check_remote=False):
+ def analyze(self, fname, find_sources=False, check_remote=False, ignore_query_strings=False):
"""Analyze links on a page."""
rv = False
self.whitelist = [re.compile(x) for x in self.site.config['LINK_CHECK_WHITELIST']]
@@ -279,19 +297,19 @@ class CommandCheck(Command):
# Check the remote link works
req_headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0 (Nikola)'} # I’m a real boy!
- resp = requests.head(target, headers=req_headers, allow_redirects=False)
+ resp = requests.head(target, headers=req_headers, allow_redirects=False, timeout=self.timeout)
# Retry client errors (4xx) as GET requests because many servers are broken
if resp.status_code >= 400 and resp.status_code <= 499:
time.sleep(0.5)
- resp = requests.get(target, headers=req_headers, allow_redirects=False)
+ resp = requests.get(target, headers=req_headers, allow_redirects=False, timeout=self.timeout)
# Follow redirects and see where they lead, redirects to errors will be reported twice
if resp.status_code in [301, 302, 307, 308]:
redir_status_code = resp.status_code
time.sleep(0.5)
# Known redirects are retested using GET because IIS servers otherwise get HEADaches
- resp = requests.get(target, headers=req_headers, allow_redirects=True)
+ resp = requests.get(target, headers=req_headers, allow_redirects=True, timeout=self.timeout)
# Permanent redirects should be updated
if redir_status_code in [301, 308]:
self.logger.warning("Remote link moved PERMANENTLY to \"{0}\" and should be updated in {1}: {2} [HTTP: {3}]".format(resp.url, filename, target, redir_status_code))
@@ -353,6 +371,10 @@ class CommandCheck(Command):
else:
target_filename_str = target_filename.decode("utf-8", errors="surrogateescape")
+ if ignore_query_strings and "?" in target_filename_str:
+ target_filename, _, _ = target_filename.rpartition("?")
+ target_filename_str, _, _ = target_filename_str.rpartition("?")
+
if any(pattern.search(target_filename_str) for pattern in self.whitelist):
continue
@@ -371,7 +393,7 @@ class CommandCheck(Command):
self.logger.error(u"Error with: {0} {1}".format(filename, exc))
return rv
- def scan_links(self, find_sources=False, check_remote=False):
+ def scan_links(self, find_sources=False, check_remote=False, ignore_query_strings=False):
"""Check links on the site."""
self.logger.debug("Checking Links:")
self.logger.debug("===============\n")
@@ -387,13 +409,13 @@ class CommandCheck(Command):
for fname in _call_nikola_list(self.site, self.cache)[0]:
if fname.startswith(output_folder):
if '.html' == fname[-5:]:
- if self.analyze(fname, find_sources, check_remote):
+ if self.analyze(fname, find_sources, check_remote, ignore_query_strings):
failure = True
if atom_extension == fname[-len(atom_extension):]:
- if self.analyze(fname, find_sources, False):
+ if self.analyze(fname, find_sources, False, ignore_query_strings):
failure = True
if fname.endswith('sitemap.xml') or fname.endswith('sitemapindex.xml'):
- if self.analyze(fname, find_sources, False):
+ if self.analyze(fname, find_sources, False, ignore_query_strings):
failure = True
if not failure:
self.logger.debug("All links checked.")
diff --git a/nikola/plugins/command/console.py b/nikola/plugins/command/console.py
index 96fee3e..18428f3 100644
--- a/nikola/plugins/command/console.py
+++ b/nikola/plugins/command/console.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Chris Warrick, Roberto Alsina and others.
+# Copyright © 2012-2024 Chris Warrick, Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/default_config.py b/nikola/plugins/command/default_config.py
index fddda26..f14c4c8 100644
--- a/nikola/plugins/command/default_config.py
+++ b/nikola/plugins/command/default_config.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -48,7 +48,10 @@ class CommandShowConfig(Command):
def _execute(self, options=None, args=None):
"""Show the default configuration."""
+ init_plugin = self.site.plugin_manager.get_plugin_by_name("init", "Command").plugin_object
+ config = init_plugin.create_configuration_to_string()
+
try:
- print(nikola.plugins.command.init.CommandInit.create_configuration_to_string())
+ print(config)
except Exception:
- sys.stdout.buffer.write(nikola.plugins.command.init.CommandInit.create_configuration_to_string().encode('utf-8'))
+ sys.stdout.buffer.write(config.encode('utf-8'))
diff --git a/nikola/plugins/command/deploy.py b/nikola/plugins/command/deploy.py
index 1896a7a..ddb5a64 100644
--- a/nikola/plugins/command/deploy.py
+++ b/nikola/plugins/command/deploy.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/github_deploy.py b/nikola/plugins/command/github_deploy.py
index 1d3e9c0..aa2da74 100644
--- a/nikola/plugins/command/github_deploy.py
+++ b/nikola/plugins/command/github_deploy.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2014-2022 Puneeth Chaganti and others.
+# Copyright © 2014-2024 Puneeth Chaganti and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/import_wordpress.py b/nikola/plugins/command/import_wordpress.py
index f3feab1..e6ec45b 100644
--- a/nikola/plugins/command/import_wordpress.py
+++ b/nikola/plugins/command/import_wordpress.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -66,7 +66,7 @@ def install_plugin(site, plugin_name, output_dir=None, show_install_notes=False)
"""Install a Nikola plugin."""
LOGGER.info("Installing plugin '{0}'".format(plugin_name))
# Get hold of the 'plugin' plugin
- plugin_installer_info = site.plugin_manager.getPluginByName('plugin', 'Command')
+ plugin_installer_info = site.plugin_manager.get_plugin_by_name('plugin', 'Command')
if plugin_installer_info is None:
LOGGER.error('Internal error: cannot find the "plugin" plugin which is supposed to come with Nikola!')
return False
@@ -236,10 +236,9 @@ to
self._find_wordpress_compiler()
if self.wordpress_page_compiler is not None:
return self.wordpress_page_compiler
- plugin_info = self.site.plugin_manager.getPluginByName('markdown', 'PageCompiler')
+ plugin_info = self.site.plugin_manager.get_plugin_by_name('markdown', 'PageCompiler')
if plugin_info is not None:
if not plugin_info.is_activated:
- self.site.plugin_manager.activatePluginByName(plugin_info.name)
plugin_info.plugin_object.set_site(self.site)
return plugin_info.plugin_object
else:
@@ -249,7 +248,7 @@ to
"""Find WordPress compiler plugin."""
if self.wordpress_page_compiler is not None:
return
- plugin_info = self.site.plugin_manager.getPluginByName('wordpress', 'PageCompiler')
+ plugin_info = self.site.plugin_manager.get_plugin_by_name('wordpress', 'PageCompiler')
if plugin_info is not None:
if not plugin_info.is_activated:
self.site.plugin_manager.activatePluginByName(plugin_info.name)
diff --git a/nikola/plugins/command/init.py b/nikola/plugins/command/init.py
index 4607758..cf22a44 100644
--- a/nikola/plugins/command/init.py
+++ b/nikola/plugins/command/init.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/new_page.py b/nikola/plugins/command/new_page.py
index 94fbc51..6587d70 100644
--- a/nikola/plugins/command/new_page.py
+++ b/nikola/plugins/command/new_page.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina, Chris Warrick and others.
+# Copyright © 2012-2024 Roberto Alsina, Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -109,5 +109,5 @@ class CommandNewPage(Command):
options['date-path'] = False
# Even though stuff was split into `new_page`, it’s easier to do it
# there not to duplicate the code.
- p = self.site.plugin_manager.getPluginByName('new_post', 'Command').plugin_object
+ p = self.site.plugin_manager.get_plugin_by_name('new_post', 'Command').plugin_object
return p.execute(options, args)
diff --git a/nikola/plugins/command/new_post.py b/nikola/plugins/command/new_post.py
index f66a188..aa96625 100644
--- a/nikola/plugins/command/new_post.py
+++ b/nikola/plugins/command/new_post.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -216,9 +216,7 @@ class CommandNewPost(Command):
def _execute(self, options, args):
"""Create a new post or page."""
global LOGGER
- compiler_names = [p.name for p in
- self.site.plugin_manager.getPluginsOfCategory(
- "PageCompiler")]
+ compiler_names = [p.name for p in self.site.plugin_manager.get_plugins_of_category("PageCompiler")]
if len(args) > 1:
print(self.help())
@@ -298,7 +296,7 @@ class CommandNewPost(Command):
self.print_compilers()
return
- compiler_plugin = self.site.plugin_manager.getPluginByName(
+ compiler_plugin = self.site.plugin_manager.get_plugin_by_name(
content_format, "PageCompiler").plugin_object
# Guess where we should put this
diff --git a/nikola/plugins/command/orphans.py b/nikola/plugins/command/orphans.py
index 169cbba..bde8425 100644
--- a/nikola/plugins/command/orphans.py
+++ b/nikola/plugins/command/orphans.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina, Chris Warrick and others.
+# Copyright © 2012-2024 Roberto Alsina, Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/plugin.py b/nikola/plugins/command/plugin.py
index ae0dead..4fd8e8f 100644
--- a/nikola/plugins/command/plugin.py
+++ b/nikola/plugins/command/plugin.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/rst2html/__init__.py b/nikola/plugins/command/rst2html/__init__.py
index 2bf329a..4fdd36f 100644
--- a/nikola/plugins/command/rst2html/__init__.py
+++ b/nikola/plugins/command/rst2html/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2015-2022 Chris Warrick and others.
+# Copyright © 2015-2024 Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -44,7 +44,7 @@ class CommandRst2Html(Command):
def _execute(self, options, args):
"""Compile reStructuredText to standalone HTML files."""
- compiler = self.site.plugin_manager.getPluginByName('rest', 'PageCompiler').plugin_object
+ compiler = self.site.plugin_manager.get_plugin_by_name('rest', 'PageCompiler').plugin_object
if len(args) != 1:
print("This command takes only one argument (input file name).")
return 2
diff --git a/nikola/plugins/command/serve.py b/nikola/plugins/command/serve.py
index cbf628c..32cd46b 100644
--- a/nikola/plugins/command/serve.py
+++ b/nikola/plugins/command/serve.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/status.py b/nikola/plugins/command/status.py
index ab6fc1e..a22f173 100644
--- a/nikola/plugins/command/status.py
+++ b/nikola/plugins/command/status.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/subtheme.py b/nikola/plugins/command/subtheme.py
index b5c5aff..c322336 100644
--- a/nikola/plugins/command/subtheme.py
+++ b/nikola/plugins/command/subtheme.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/theme.py b/nikola/plugins/command/theme.py
index 2f99dd8..f7608b5 100644
--- a/nikola/plugins/command/theme.py
+++ b/nikola/plugins/command/theme.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina, Chris Warrick and others.
+# Copyright © 2012-2024 Roberto Alsina, Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/version.py b/nikola/plugins/command/version.py
index 4ec8a46..bdc1357 100644
--- a/nikola/plugins/command/version.py
+++ b/nikola/plugins/command/version.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated