| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import logging
- import shutil
- import tempfile
- import sys
- from os.path import isfile, join
- from mkdocs.commands.build import build
- from mkdocs.config import load_config
- log = logging.getLogger(__name__)
- def _init_asyncio_patch():
- """
- Select compatible event loop for Tornado 5+.
- As of Python 3.8, the default event loop on Windows is `proactor`,
- however Tornado requires the old default "selector" event loop.
- As Tornado has decided to leave this to users to set, MkDocs needs
- to set it. See https://github.com/tornadoweb/tornado/issues/2608.
- """
- if sys.platform.startswith("win") and sys.version_info >= (3, 8):
- import asyncio
- try:
- from asyncio import WindowsSelectorEventLoopPolicy
- except ImportError:
- pass # Can't assign a policy which doesn't exist.
- else:
- if not isinstance(asyncio.get_event_loop_policy(), WindowsSelectorEventLoopPolicy):
- asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
- def _get_handler(site_dir, StaticFileHandler):
- from tornado.template import Loader
- class WebHandler(StaticFileHandler):
- def write_error(self, status_code, **kwargs):
- if status_code in (404, 500):
- error_page = '{}.html'.format(status_code)
- if isfile(join(site_dir, error_page)):
- self.write(Loader(site_dir).load(error_page).generate())
- else:
- super().write_error(status_code, **kwargs)
- return WebHandler
- def _livereload(host, port, config, builder, site_dir):
- # We are importing here for anyone that has issues with livereload. Even if
- # this fails, the --no-livereload alternative should still work.
- _init_asyncio_patch()
- from livereload import Server
- import livereload.handlers
- class LiveReloadServer(Server):
- def get_web_handlers(self, script):
- handlers = super().get_web_handlers(script)
- # replace livereload handler
- return [(handlers[0][0], _get_handler(site_dir, livereload.handlers.StaticFileHandler), handlers[0][2],)]
- server = LiveReloadServer()
- # Watch the documentation files, the config file and the theme files.
- server.watch(config['docs_dir'], builder)
- server.watch(config['config_file_path'], builder)
- for d in config['theme'].dirs:
- server.watch(d, builder)
- # Run `serve` plugin events.
- server = config['plugins'].run_event('serve', server, config=config, builder=builder)
- server.serve(root=site_dir, host=host, port=port, restart_delay=0)
- def _static_server(host, port, site_dir):
- # Importing here to separate the code paths from the --livereload
- # alternative.
- _init_asyncio_patch()
- from tornado import ioloop
- from tornado import web
- application = web.Application([
- (r"/(.*)", _get_handler(site_dir, web.StaticFileHandler), {
- "path": site_dir,
- "default_filename": "index.html"
- }),
- ])
- application.listen(port=port, address=host)
- log.info('Running at: http://%s:%s/', host, port)
- log.info('Hold ctrl+c to quit.')
- try:
- ioloop.IOLoop.instance().start()
- except KeyboardInterrupt:
- log.info('Stopping server...')
- def serve(config_file=None, dev_addr=None, strict=None, theme=None,
- theme_dir=None, livereload='livereload', **kwargs):
- """
- Start the MkDocs development server
- By default it will serve the documentation on http://localhost:8000/ and
- it will rebuild the documentation and refresh the page automatically
- whenever a file is edited.
- """
- # Create a temporary build directory, and set some options to serve it
- # PY2 returns a byte string by default. The Unicode prefix ensures a Unicode
- # string is returned. And it makes MkDocs temp dirs easier to identify.
- site_dir = tempfile.mkdtemp(prefix='mkdocs_')
- def builder():
- log.info("Building documentation...")
- config = load_config(
- config_file=config_file,
- dev_addr=dev_addr,
- strict=strict,
- theme=theme,
- theme_dir=theme_dir,
- site_dir=site_dir,
- **kwargs
- )
- # Override a few config settings after validation
- config['site_url'] = 'http://{}/'.format(config['dev_addr'])
- live_server = livereload in ['dirty', 'livereload']
- dirty = livereload == 'dirty'
- build(config, live_server=live_server, dirty=dirty)
- return config
- try:
- # Perform the initial build
- config = builder()
- host, port = config['dev_addr']
- if livereload in ['livereload', 'dirty']:
- _livereload(host, port, config, builder, site_dir)
- else:
- _static_server(host, port, site_dir)
- finally:
- shutil.rmtree(site_dir)
|