Skip to content

Commit

Permalink
Provide an Elsa class that can be used programmatically
Browse files Browse the repository at this point in the history
Related: pyvec#32
  • Loading branch information
hroncok committed Jun 28, 2017
1 parent 9672d65 commit 3205f98
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 127 deletions.
4 changes: 2 additions & 2 deletions elsa/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from ._cli import cli
from ._cli import Elsa, cli

__all__ = ['cli']
__all__ = ['Elsa', 'cli']
321 changes: 196 additions & 125 deletions elsa/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,132 +11,203 @@
from ._shutdown import ShutdownableFreezer, inject_shutdown


def port_option():
return click.option(
'--port', type=int, default=8003,
help='Port to listen at')


def cname_option():
return click.option(
'--cname/--no-cname', default=True,
help='Whether to create the CNAME file, default is to create it')


def path_option(app):
return click.option(
'--path', default=os.path.join(app.root_path, '_build'),
help='Input path, default _build')


def freeze_app(app, freezer, path, base_url):
if not base_url:
raise click.UsageError('No base URL provided, use --base-url')
print('Generating HTML...')
app.config['FREEZER_DESTINATION'] = path
app.config['FREEZER_BASE_URL'] = base_url
app.config['SERVER_NAME'] = urllib.parse.urlparse(base_url).netloc

# make sure Frozen Flask warnings are treated as errors
warnings.filterwarnings('error', category=flask_frozen.FrozenFlaskWarning)

try:
freezer.freeze()
except flask_frozen.FrozenFlaskWarning as w:
print('Error:', w, file=sys.stderr)
sys.exit(1)


def inject_cname(app):
"""Create CNAME route for GitHub pages"""
@app.route('/CNAME')
def cname():
return Response(app.config['SERVER_NAME'],
mimetype='application/octet-stream')
class Elsa:
"""Elsa freezes websites written in Flask"""

DEFAULT_PORT = 8003
DEFAULT_REMOTE = 'origin'

def __init__(self, app, *, freezer=None, base_url=None):
"""Initialize Elsa.
Arguments:
* app: and instance of Flask WSGI application
* freezer: flask_frozen.Freezer-like instance (optional)
* base_url: URL for the application, used for external links (optional)
"""
self.app = app
self.freezer = freezer or ShutdownableFreezer(app)
self.base_url = base_url

def _base_url_help(self, *, freeze=False):
freeze = ' with --freeze' if freeze else ''
url = self.base_url
default = 'default {}'.format(url) if url else 'mandatory' + freeze
return 'URL for the application, used for external links, ' + default

def _inject_cname(self):
"""Create CNAME route for GitHub pages"""
@self.app.route('/CNAME')
def cname():
return Response(self.app.config['SERVER_NAME'],
mimetype='application/octet-stream')

def _inject_shutdown(self):
"""Create a shutdown route"""
inject_shutdown(self.app)

def freeze(self, path=None, base_url=None):
"""Freeze the app
Arguments:
* path: location of frozen website (tries FREEZER_DESTINATION app
config if not provided)
* base_url: URL for the application, used for external links
(tries self.base_url and FREEZER_BASE_URL app config
if not provided)"""
base_url = (base_url or self.base_url or
self.app.config.get('FREEZER_BASE_URL'))
if not base_url:
raise ValueError('No base URL provided!')
self.app.config['FREEZER_BASE_URL'] = base_url

path = path or self.app.config.get('FREEZER_DESTINATION')
self.app.config['FREEZER_DESTINATION'] = path
self.app.config['SERVER_NAME'] = urllib.parse.urlparse(base_url).netloc

if not path:
raise ValueError('No path provided!')

# make sure Frozen Flask warnings are treated as errors
warnings.filterwarnings('error',
category=flask_frozen.FrozenFlaskWarning)

print('Generating HTML...')
self.freezer.freeze()

def freeze_fail_exit(self, path=None, base_url=None):
"""Like freeze() but exists on exception"""
try:
self.freeze(path, base_url)
except flask_frozen.FrozenFlaskWarning as w:
print('Error:', w, file=sys.stderr)
sys.exit(1)

def serve(self, port=DEFAULT_PORT):
"""Serve the frozen app using the freezers ability to serve what was
frozen"""
return self.freezer.serve(port=port)

def deploy(self, *, path=None, remote=None, push=False, show_err=False):
"""Deploy to GitHub pages, expects to be already frozen
Arguments:
* path: Where to find frozen HTML, defaults to FREEZER_DESTINATION app
config
* remote: (optional) git remote
* push: whether to push to git
* show_err: whether to display git push failure stderr"""
path = path or self.app.config.get('FREEZER_DESTINATION')
remote = remote or self.DEFAULT_REMOTE
return deploy_(path, remote=remote, push=push,
show_err=show_err)

@classmethod
def _port_option(cls):
return click.option(
'--port', type=int, default=cls.DEFAULT_PORT,
help='Port to listen at')

@classmethod
def _cname_option(cls):
return click.option(
'--cname/--no-cname', default=True,
help='Whether to create the CNAME file, default is to create it')

def _path_option(self):
return click.option(
'--path', default=os.path.join(self.app.root_path, '_build'),
help='Input path, default _build')

def cli(self):
"""Get a cli() function"""

@click.group(context_settings=dict(help_option_names=['-h', '--help']),
help=__doc__)
def command():
pass

@command.command()
@self._port_option()
@self._cname_option()
def serve(port, cname):
"""Run a debug server"""

# Workaround for https://github.com/pallets/flask/issues/1907
auto_reload = self.app.config.get('TEMPLATES_AUTO_RELOAD')
if auto_reload or auto_reload is None:
self.app.jinja_env.auto_reload = True

self._inject_shutdown()
if cname:
self._inject_cname()

self.app.run(host='0.0.0.0', port=port, debug=True)

@command.command()
@self._path_option()
@click.option('--base-url', default=self.base_url,
help=self._base_url_help())
@click.option('--serve/--no-serve',
help='After building the site, run a server with it')
@self._port_option()
@self._cname_option()
def freeze(path, base_url, serve, port, cname):
"""Build a static site"""
if cname:
self._inject_cname()

if not base_url:
raise click.UsageError('No base URL provided, use --base-url')
self.freeze_fail_exit(path, base_url)

if serve:
self.serve(port=port)

@command.command()
@self._path_option()
@click.option('--base-url', default=self.base_url,
help=self._base_url_help(freeze=True))
@click.option('--remote', default=self.DEFAULT_REMOTE,
help='The name of the remote to push to, '
'default origin')
@click.option('--push/--no-push', default=None,
help='Whether to push the gh-pages branch, '
'deprecated default is to push')
@click.option('--freeze/--no-freeze', default=True,
help='Whether to freeze the site before deploying, '
'default is to freeze')
@click.option('--show-git-push-stderr', is_flag=True,
help='Show the stderr output of `git push` failure, '
'might be dangerous if logs are public')
@self._cname_option()
def deploy(path, base_url, remote, push, freeze,
show_git_push_stderr, cname):
"""Deploy the site to GitHub pages"""
if push is None:
warnings.simplefilter('always')
msg = ('Using deploy without explicit --push/--no-push is '
'deprecated. Assuming --push for now. In future '
'versions of elsa, the deploy command will not push to '
'the remote server by default. Use --push explicitly '
'to maintain current behavior.')
warnings.warn(msg, DeprecationWarning)
push = True
if freeze:
if cname:
self._inject_cname()
if not base_url:
raise click.UsageError('No base URL provided, use '
'--base-url')
self.freeze(path, base_url)

deploy_(path, remote=remote, push=push,
show_err=show_git_push_stderr)

return command()


def cli(app, *, freezer=None, base_url=None):
"""Get a cli() function for provided app"""
if not freezer:
freezer = ShutdownableFreezer(app)

@click.group(context_settings=dict(help_option_names=['-h', '--help']),
help=__doc__)
def command():
pass

@command.command()
@port_option()
@cname_option()
def serve(port, cname):
"""Run a debug server"""

# Workaround for https://github.com/pallets/flask/issues/1907
auto_reload = app.config.get('TEMPLATES_AUTO_RELOAD')
if auto_reload or auto_reload is None:
app.jinja_env.auto_reload = True

inject_shutdown(app)
if cname:
inject_cname(app)

app.run(host='0.0.0.0', port=port, debug=True)

@command.command()
@path_option(app)
@click.option('--base-url', default=base_url,
help='URL for the application, used for external links, ' +
('default {}'.format(base_url) if base_url else 'mandatory'))
@click.option('--serve/--no-serve',
help='After building the site, run a server with it')
@port_option()
@cname_option()
def freeze(path, base_url, serve, port, cname):
"""Build a static site"""
if cname:
inject_cname(app)

freeze_app(app, freezer, path, base_url)

if serve:
freezer.serve(port=port)

@command.command()
@path_option(app)
@click.option('--base-url', default=base_url,
help='URL for the application, used for external links, ' +
('default {}'.format(base_url) if base_url else 'mandatory'
' with --freeze'))
@click.option('--remote', default='origin',
help='The name of the remote to push to, '
'default origin')
@click.option('--push/--no-push', default=None,
help='Whether to push the gh-pages branch, '
'deprecated default is to push')
@click.option('--freeze/--no-freeze', default=True,
help='Whether to freeze the site before deploying, '
'default is to freeze')
@click.option('--show-git-push-stderr', is_flag=True,
help='Show the stderr output of `git push` failure, '
'might be dangerous if logs are public')
@cname_option()
def deploy(path, base_url, remote, push, freeze,
show_git_push_stderr, cname):
"""Deploy the site to GitHub pages"""
if push is None:
warnings.simplefilter('always')
msg = ('Using deploy without explicit --push/--no-push is '
'deprecated. Assuming --push for now. In future versions '
'of elsa, the deploy command will not push to the remote '
'server by default. Use --push explicitly to maintain '
'current behavior.')
warnings.warn(msg, DeprecationWarning)
push = True
if freeze:
if cname:
inject_cname(app)
freeze_app(app, freezer, path, base_url)

deploy_(path, remote=remote, push=push, show_err=show_git_push_stderr)

return command()
return Elsa(app, freezer=freezer, base_url=base_url).cli()

0 comments on commit 3205f98

Please sign in to comment.