Skip to content

Commit

Permalink
Merge pull request #70 from Goutte/feat-69-skip-existing-files
Browse files Browse the repository at this point in the history
Feature Implementation : Skip existing files
  • Loading branch information
tswast committed Jan 26, 2017
2 parents dac9355 + a54820d commit 9169d6f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 25 deletions.
12 changes: 10 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,8 @@ are accepted:
.. versionadded:: 0.12

``FREEZER_IGNORE_404_NOT_FOUND``
If set to ``True`` (defaults False), Frozen-Flask won't stop freezing when
404 error is returned by your application.
If set to ``True`` (defaults ``False``), Frozen-Flask won't stop freezing
when a 404 error is returned by your application.
In this case, a warning will be printed on stdout and the static page will
be generated using your 404 error page handler or flask's default one.
This can be useful during development phase if you have already referenced
Expand All @@ -303,6 +303,14 @@ are accepted:

.. versionadded:: 0.13

``FREEZER_SKIP_EXISTING``
If set to ``True`` (defaults ``False``), Frozen-Flask will skip the
generation of files that already exist in the build directory,
even if the contents would have been different. Useful if your generation
takes up a very long time and you only want to generate new files.

.. versionadded:: 0.14

.. _mime-types:

Filenames and MIME types
Expand Down
34 changes: 21 additions & 13 deletions flask_frozen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,15 @@ class MissingURLGeneratorWarning(Warning):
class MimetypeMismatchWarning(Warning):
pass


class NotFoundWarning(Warning):
pass


class RedirectWarning(Warning):
pass


class Freezer(object):
"""
:param app: your application or None if you use :meth:`init_app`
Expand Down Expand Up @@ -107,9 +110,11 @@ def init_app(self, app):
app.config.setdefault('FREEZER_RELATIVE_URLS', False)
app.config.setdefault('FREEZER_IGNORE_404_NOT_FOUND', False)
app.config.setdefault('FREEZER_REDIRECT_POLICY', 'follow')
app.config.setdefault('FREEZER_SKIP_EXISTING', False)

def register_generator(self, function):
"""Register a function as an URL generator.
"""
Register a function as an URL generator.
The function should return an iterable of URL paths or
``(endpoint, values)`` tuples to be used as
Expand Down Expand Up @@ -190,7 +195,7 @@ def _script_name(self):

def _generate_all_urls(self):
"""
Run all generators and yield (url, enpoint) tuples.
Run all generators and yield (url, endpoint) tuples.
"""
script_name = self._script_name()
url_encoding = self.app.url_map.charset
Expand Down Expand Up @@ -233,7 +238,7 @@ def _generate_all_urls(self):

def _check_endpoints(self, seen_endpoints):
"""
Warn if some of the app's enpoints are not in seen_endpoints.
Warn if some of the app's endpoints are not in seen_endpoints.
"""
get_endpoints = set(
rule.endpoint for rule in self.app.url_map.iter_rules()
Expand All @@ -253,14 +258,20 @@ def _check_endpoints(self, seen_endpoints):
stacklevel=3)

def _build_one(self, url):
"""Get the given ``url`` from the app and write the matching file.
"""
"""Get the given ``url`` from the app and write the matching file."""
client = self.app.test_client()
base_url = self.app.config['FREEZER_BASE_URL']
redirect_policy = self.app.config['FREEZER_REDIRECT_POLICY']
follow_redirects = redirect_policy == 'follow'
ignore_redirect = redirect_policy == 'ignore'

destination_path = self.urlpath_to_filepath(url)
filename = os.path.join(self.root, *destination_path.split('/'))

skip = self.app.config['FREEZER_SKIP_EXISTING']
if skip and os.path.isfile(filename):
return filename

with conditional_context(self.url_for_logger, self.log_url_for):
with conditional_context(patch_url_for(self.app),
self.app.config['FREEZER_RELATIVE_URLS']):
Expand All @@ -269,7 +280,7 @@ def _build_one(self, url):

# The client follows redirects by itself
# Any other status code is probably an error
# except we explictly want 404 errors to be skipped
# except we explicitly want 404 errors to be skipped
# (eg. while application is in development)
ignore_404 = self.app.config['FREEZER_IGNORE_404_NOT_FOUND']
if response.status_code != 200:
Expand All @@ -285,9 +296,6 @@ def _build_one(self, url):
raise ValueError('Unexpected status %r on URL %s' \
% (response.status, url))

destination_path = self.urlpath_to_filepath(url)
filename = os.path.join(self.root, *destination_path.split('/'))

if not self.app.config['FREEZER_IGNORE_MIMETYPE_WARNINGS']:
# Most web servers guess the mime type of static files by their
# filename. Check that this guess is consistent with the actual
Expand Down Expand Up @@ -337,7 +345,8 @@ def urlpath_to_filepath(self, path):
return path[1:]

def serve(self, **options):
"""Run an HTTP server on the result of the build.
"""
Run an HTTP server on the result of the build.
:param options: passed to :meth:`flask.Flask.run`.
"""
Expand Down Expand Up @@ -446,10 +455,10 @@ def walk(directory, path_so_far):

@contextmanager
def patch_url_for(app):
"""Patches ``url_for`` in Jinja globals to use :func:`relative_url_for`.
"""
Patches ``url_for`` in Jinja globals to use :func:`relative_url_for`.
This is a context manager, to be used in a ``with`` statement.
"""
previous_url_for = app.jinja_env.globals['url_for']
app.jinja_env.globals['url_for'] = relative_url_for
Expand All @@ -476,7 +485,6 @@ def relative_url_for(endpoint, **values):
If the ``FREEZER_RELATIVE_URLS`` `configuration`_ is True, Frozen-Flask
will automatically patch the application's Jinja environment so that
``url_for`` in templates is this function.
"""
url = url_for(endpoint, **values)

Expand Down
33 changes: 23 additions & 10 deletions flask_frozen/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
~~~~~~~~~~~~~~~~~~
Automated test suite for Frozen-Flask
Run with :
$ python -m flask_frozen.tests
:copyright: (c) 2010-2012 by Simon Sapin.
:license: BSD, see LICENSE for more details.
Expand Down Expand Up @@ -159,7 +161,7 @@ class TestFreezer(unittest.TestCase):
maxDiff = None

def do_extra_config(self, app, freezer):
pass # To be overriden
pass # To be overridden

@contextmanager
def make_app(self):
Expand Down Expand Up @@ -209,7 +211,7 @@ def test_all_urls_method(self):
def test_built_urls(self):
with self.built_app() as (temp, app, freezer, urls):
self.assertEqual(set(urls), set(self.expected_output))
# Make sure it was not accidently used as a destination
# Make sure it was not accidentally used as a destination
default = os.path.join(os.path.dirname(__file__), 'build')
self.assertTrue(not os.path.exists(default))

Expand Down Expand Up @@ -304,7 +306,7 @@ def test_warn_on_internal_404(self):
freezer.freeze()
self.assertEqual(len(logged_warnings), 1)
self.assertEqual(logged_warnings[0].category,
NotFoundWarning)
NotFoundWarning)

def test_error_on_redirect(self):
with self.make_app() as (temp, app, freezer):
Expand All @@ -328,7 +330,7 @@ def test_warn_on_redirect(self):
freezer.freeze()
self.assertEqual(len(logged_warnings), 1)
self.assertEqual(logged_warnings[0].category,
RedirectWarning)
RedirectWarning)

def test_warn_on_missing_generator(self):
with self.make_app() as (temp, app, freezer):
Expand All @@ -342,7 +344,7 @@ def external_url(some_argument):
freezer.freeze()
self.assertEqual(len(logged_warnings), 1)
self.assertEqual(logged_warnings[0].category,
MissingURLGeneratorWarning)
MissingURLGeneratorWarning)

def test_wrong_default_mimetype(self):
with self.make_app() as (temp, app, freezer):
Expand All @@ -355,7 +357,7 @@ def no_extension():
freezer.freeze()
self.assertEqual(len(logged_warnings), 1)
self.assertEqual(logged_warnings[0].category,
MimetypeMismatchWarning)
MimetypeMismatchWarning)

def test_default_mimetype(self):
with self.make_app() as (temp, app, freezer):
Expand All @@ -366,7 +368,7 @@ def no_extension():

def test_unknown_extension(self):
with self.make_app() as (temp, app, freezer):
@app.route(u'/unkown-extension.fuu')
@app.route(u'/unknown-extension.fuu')
def no_extension():
return '42', 200, {'Content-Type': 'application/octet-stream'}
freezer.freeze()
Expand All @@ -390,7 +392,19 @@ def no_extension():
freezer.freeze()
self.assertEqual(len(logged_warnings), 1)
self.assertEqual(logged_warnings[0].category,
MimetypeMismatchWarning)
MimetypeMismatchWarning)

def test_skip_existing_files(self):
with self.make_app() as (temp, app, freezer):
app.config['FREEZER_SKIP_EXISTING'] = True
with open(os.path.join(temp, 'skipped.html'), 'w') as f:
f.write("6*9")
@app.route(u'/skipped.html')
def skipped():
return '42'
freezer.freeze()
with open(os.path.join(temp, 'skipped.html')) as f:
self.assertEqual(f.read(), '6*9')


class TestInitApp(TestFreezer):
Expand All @@ -414,7 +428,7 @@ def do_extra_config(self, app, freezer):

class TestNonexsistentDestination(TestFreezer):
def do_extra_config(self, app, freezer):
# frozen/htdocs does not exsist in the newly created temp directory,
# frozen/htdocs does not exist in the newly created temp directory,
# the Freezer has to create it.
app.config['FREEZER_DESTINATION'] = os.path.join(
app.config['FREEZER_DESTINATION'], 'frozen', 'htdocs')
Expand All @@ -428,7 +442,6 @@ def do_extra_config(self, app, freezer):
b'/where_am_i/ http://example.net/where_am_i/')



class TestWithoutUrlForLog(TestFreezer):
freezer_kwargs = dict(log_url_for=False)

Expand Down

0 comments on commit 9169d6f

Please sign in to comment.