@@ -6,8 +6,7 @@ from hyde.fs import File, Folder | |||
from hyde.template import Template | |||
from jinja2 import contextfunction, Environment, FileSystemLoader, Undefined | |||
class LoyalUndefined(Undefined): | |||
class SilentUndefined(Undefined): | |||
def __getattr__(self, name): | |||
return self | |||
@@ -45,12 +44,11 @@ class Jinja2Template(Template): | |||
if config: | |||
loader = FileSystemLoader([ | |||
str(config.content_root_path), | |||
str(config.media_root_path), | |||
str(config.layout_root_path), | |||
]) | |||
else: | |||
loader = FileSystemLoader(str(self.sitepath)) | |||
self.env = Environment(loader=loader, undefined=LoyalUndefined) | |||
self.env = Environment(loader=loader, undefined=SilentUndefined) | |||
self.env.globals['media_url'] = media_url | |||
self.env.globals['content_url'] = content_url | |||
@@ -171,12 +171,27 @@ class File(FS): | |||
(mime, encoding) = mimetypes.guess_type(self.path) | |||
return mime | |||
@property | |||
def is_binary(self): | |||
"""Return true if this is a binary file.""" | |||
with open(self.path, 'rb') as fin: | |||
CHUNKSIZE = 1024 | |||
while 1: | |||
chunk = fin.read(CHUNKSIZE) | |||
if '\0' in chunk: | |||
return True | |||
if len(chunk) < CHUNKSIZE: | |||
break | |||
return False | |||
@property | |||
def is_text(self): | |||
return self.mimetype.split("/")[0] == "text" | |||
"""Return true if this is a text file.""" | |||
return (not self.is_binary) | |||
@property | |||
def is_image(self): | |||
"""Return true if this is an image file.""" | |||
return self.mimetype.split("/")[0] == "image" | |||
def read_all(self, encoding='utf-8'): | |||
@@ -3,6 +3,7 @@ The generator class and related utility functions. | |||
""" | |||
from hyde.exceptions import HydeException | |||
from hyde.fs import File | |||
from hyde.plugin import Plugin | |||
from hyde.template import Template | |||
from contextlib import contextmanager | |||
@@ -24,6 +25,30 @@ class Generator(object): | |||
self.site = site | |||
self.__context__ = dict(site=site) | |||
self.template = None | |||
Plugin.load_all(site) | |||
class PluginProxy(object): | |||
""" | |||
A proxy class to raise events in registered plugins | |||
""" | |||
def __init__(self, site): | |||
super(PluginProxy, self).__init__() | |||
self.site = site | |||
def __getattr__(self, method_name): | |||
if hasattr(Plugin, method_name): | |||
def __call_plugins__(*args, **kwargs): | |||
if self.site.plugins: | |||
for plugin in self.site.plugins: | |||
if hasattr(plugin, method_name): | |||
function = getattr(plugin, method_name) | |||
function(*args, **kwargs) | |||
return __call_plugins__ | |||
raise HydeException( | |||
"Unknown plugin method [%s] called." % method_name) | |||
self.events = PluginProxy(self.site) | |||
@contextmanager | |||
def context_for_resource(self, resource): | |||
@@ -37,7 +62,7 @@ class Generator(object): | |||
yield self.__context__ | |||
self.__context__.update(resource=None) | |||
def initialize_template_if_needed(self): | |||
def load_template_if_needed(self): | |||
""" | |||
Loads and configures the template environement from the site | |||
configuration if its not done already. | |||
@@ -50,7 +75,16 @@ class Generator(object): | |||
logger.info("Configuring the template environment") | |||
self.template.configure(self.site.config) | |||
def reload_if_needed(self): | |||
self.events.template_loaded(self.template) | |||
def initialize(self): | |||
""" | |||
Start Generation. Perform setup tasks and inform plugins. | |||
""" | |||
logger.info("Begin Generation") | |||
self.events.begin_generation() | |||
def load_site_if_needed(self): | |||
""" | |||
Checks if the site requries a reload and loads if | |||
necessary. | |||
@@ -60,25 +94,35 @@ class Generator(object): | |||
logger.info("Reading site contents") | |||
self.site.load() | |||
def finalize(self): | |||
""" | |||
Generation complete. Inform plugins and cleanup. | |||
""" | |||
logger.info("Generation Complete") | |||
self.events.generation_complete() | |||
def generate_all(self): | |||
""" | |||
Generates the entire website | |||
""" | |||
logger.info("Reading site contents") | |||
self.initialize_template_if_needed() | |||
self.reload_if_needed() | |||
self.load_template_if_needed() | |||
self.initialize() | |||
self.load_site_if_needed() | |||
self.events.begin_site() | |||
logger.info("Generating site to [%s]" % | |||
self.site.config.deploy_root_path) | |||
self.__generate_node__(self.site.content) | |||
self.events.site_complete() | |||
self.finalize() | |||
def generate_node_at_path(self, node_path=None): | |||
""" | |||
Generates a single node. If node_path is non-existent or empty, | |||
generates the entire site. | |||
""" | |||
self.initialize_template_if_needed() | |||
self.reload_if_needed() | |||
self.load_template_if_needed() | |||
self.load_site_if_needed() | |||
node = None | |||
if node_path: | |||
node = self.site.content.node_from_path(node_path) | |||
@@ -89,12 +133,16 @@ class Generator(object): | |||
Generates the given node. If node is invalid, empty or | |||
non-existent, generates the entire website. | |||
""" | |||
self.initialize_template_if_needed() | |||
self.reload_if_needed() | |||
if not node: | |||
return self.generate_all() | |||
self.load_template_if_needed() | |||
self.initialize() | |||
self.load_site_if_needed() | |||
try: | |||
self.__generate_node__(node) | |||
self.finalize() | |||
except HydeException: | |||
self.generate_all() | |||
@@ -103,31 +151,39 @@ class Generator(object): | |||
Generates a single resource. If resource_path is non-existent or empty, | |||
generats the entire website. | |||
""" | |||
self.initialize_template_if_needed() | |||
self.reload_if_needed() | |||
self.load_template_if_needed() | |||
self.load_site_if_needed() | |||
resource = None | |||
if resource_path: | |||
resource = self.site.content.resource_from_path(resource_path) | |||
return self.generate_resource(resource) | |||
self.generate_resource(resource) | |||
def generate_resource(self, resource=None): | |||
""" | |||
Generates the given resource. If resource is invalid, empty or | |||
non-existent, generates the entire website. | |||
""" | |||
self.initialize_template_if_needed() | |||
self.reload_if_needed() | |||
if not resource: | |||
return self.generate_all() | |||
self.load_template_if_needed() | |||
self.initialize() | |||
self.load_site_if_needed() | |||
try: | |||
self.__generate_resource__(resource) | |||
self.finalize() | |||
except HydeException: | |||
self.generate_all() | |||
def __generate_node__(self, node): | |||
logger.info("Generating [%s]", node) | |||
for resource in node.walk_resources(): | |||
self.__generate_resource__(resource) | |||
for node in node.walk(): | |||
self.events.begin_node(node) | |||
for resource in node.resources: | |||
self.__generate_resource__(resource) | |||
self.events.node_complete(node) | |||
def __generate_resource__(self, resource): | |||
logger.info("Processing [%s]", resource) | |||
@@ -137,10 +193,15 @@ class Generator(object): | |||
self.site.content.source_folder)) | |||
target.parent.make() | |||
if resource.source_file.is_text: | |||
text = resource.source_file.read_all() | |||
text = self.events.begin_text_resource(resource, text) or text | |||
logger.info("Rendering [%s]", resource) | |||
text = self.template.render(resource.source_file.read_all(), | |||
context) | |||
text = self.template.render(text, context) | |||
text = self.events.text_resource_complete( | |||
resource, text) or text | |||
target.write(text) | |||
else: | |||
logger.info("Copying binary file [%s]", resource) | |||
self.events.begin_binary_resource(resource) | |||
resource.source_file.copy_to(target) | |||
self.events.binary_resource_complete(resource) |
@@ -5,6 +5,11 @@ import sys | |||
from hyde.exceptions import HydeException | |||
import logging | |||
from logging import NullHandler | |||
logger = logging.getLogger('hyde.engine') | |||
logger.addHandler(NullHandler()) | |||
plugins = {} | |||
templates = {} | |||
@@ -17,6 +22,7 @@ def load_python_object(name): | |||
if module_name == '': | |||
(module_name, object_name) = (object_name, module_name) | |||
try: | |||
logger.info('Loading module [%s]' % module_name) | |||
module = __import__(module_name) | |||
except ImportError: | |||
raise HydeException("The given module name [%s] is invalid." % | |||
@@ -32,11 +38,13 @@ def load_python_object(name): | |||
module_name) | |||
try: | |||
logger.info('Getting object [%s] from module [%s]' % | |||
(object_name, module_name)) | |||
return getattr(module, object_name) | |||
except AttributeError: | |||
raise HydeException("Cannot load the specified plugin [%s]. " | |||
"The given module [%s] does not contain the " | |||
"desired object [%s]. Please fix the" | |||
"desired object [%s]. Please fix the " | |||
"configuration or ensure that the module is " | |||
"installed properly" % | |||
(name, module_name, object_name)) |
@@ -44,7 +44,8 @@ class Config(Expando): | |||
media_root='media', | |||
layout_root='layout', | |||
media_url='/media', | |||
site_url='/' | |||
site_url='/', | |||
plugins = [] | |||
) | |||
conf = dict(**default_config) | |||
if config_dict: | |||
@@ -2,12 +2,14 @@ | |||
""" | |||
Contains definition for a plugin protocol and other utiltities. | |||
""" | |||
import abc | |||
from hyde import loader | |||
class Plugin(object): | |||
""" | |||
The plugin protocol | |||
""" | |||
__metaclass__ = abc.ABCMeta | |||
def __init__(self, site): | |||
super(Plugin, self).__init__() | |||
@@ -19,60 +21,105 @@ class Plugin(object): | |||
""" | |||
pass | |||
def prepare_site(self): | |||
def begin_generation(self): | |||
""" | |||
Called when generation is about to take place. | |||
""" | |||
pass | |||
def site_load_complete(self): | |||
def begin_site(self): | |||
""" | |||
Called when the site is built complete. This implies that all the | |||
Called when the site is loaded completely. This implies that all the | |||
nodes and resources have been identified and are accessible in the | |||
site variable. | |||
""" | |||
pass | |||
def prepare_node(self, node): | |||
def begin_node(self, node): | |||
""" | |||
Called when a node is about to be processed for generation. | |||
This method is called only when the entire node is generated. | |||
""" | |||
pass | |||
def prepare_resource(self, resource, text): | |||
def begin_text_resource(self, resource, text): | |||
""" | |||
Called when a resource is about to be processed for generation. | |||
The `text` parameter contains the, resource text at this point | |||
Called when a text resource is about to be processed for generation. | |||
The `text` parameter contains the resource text at this point | |||
in its lifecycle. It is the text that has been loaded and any | |||
plugins that are higher in the order may have tampered with it. | |||
But the text has not been processed by the template yet. | |||
But the text has not been processed by the template yet. Note that | |||
the source file associated with the text resource may not be modifed | |||
by any plugins. | |||
If this function returns a value, it is used as the text for further | |||
processing. | |||
""" | |||
return text | |||
def process_resource(self, resource, text): | |||
def begin_binary_resource(self, resource): | |||
""" | |||
Called when a binary resource is about to be processed for generation. | |||
Plugins are free to modify the contents of the file. | |||
""" | |||
pass | |||
def text_resource_complete(self, resource, text): | |||
""" | |||
Called when a resource has been processed by the template. | |||
The `text` parameter contains the, resource text at this point | |||
The `text` parameter contains the resource text at this point | |||
in its lifecycle. It is the text that has been processed by the | |||
template and any plugins that are higher in the order may have | |||
tampered with it. | |||
tampered with it. Note that the source file associated with the | |||
text resource may not be modifed by any plugins. | |||
If this function returns a value, it is used as the text for further | |||
processing. | |||
""" | |||
return text | |||
def binary_resource_complete(self, resource): | |||
""" | |||
Called when a binary resource has already been processed. | |||
Plugins are free to modify the contents of the file. | |||
""" | |||
pass | |||
def node_complete(self, node): | |||
""" | |||
Called when all the resources in the node have been processed. | |||
This method is called only when the entire node is generated. | |||
""" | |||
pass | |||
def site_complete(self): | |||
""" | |||
Called when the entire site has been processed. This method is called | |||
only when the entire site is generated. | |||
""" | |||
pass | |||
def site_complete(self): | |||
""" | |||
Called when the entire site has been processed. | |||
Called when the generation process is complete. This method is called | |||
only when the entire site is generated. | |||
""" | |||
pass | |||
def generation_complete(self): | |||
""" | |||
Called when generation is completed. | |||
""" | |||
pass | |||
@staticmethod | |||
def load_all(site): | |||
""" | |||
Loads plugins based on the configuration. Assigns the plugins to | |||
'site.plugins' | |||
""" | |||
site.plugins = [loader.load_python_object(name)(site) | |||
for name in site.config.plugins] |
@@ -275,6 +275,7 @@ class Site(object): | |||
self.sitepath = Folder(str(sitepath)) | |||
self.config = config if config else Config(self.sitepath) | |||
self.content = RootNode(self.config.content_root_path, self) | |||
self.plugins = [] | |||
def load(self): | |||
""" | |||
@@ -1,24 +1,24 @@ | |||
<?xml version="1.0"?> | |||
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> | |||
<cross-domain-policy> | |||
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html --> | |||
<!-- Most restrictive policy: --> | |||
<site-control permitted-cross-domain-policies="none"/> | |||
<site-control permitted-cross-domain-policies="none"/> | |||
<!-- Least restrictive policy: --> | |||
<!-- | |||
<site-control permitted-cross-domain-policies="all"/> | |||
<allow-access-from domain="*" to-ports="*" secure="false"/> | |||
<allow-http-request-headers-from domain="*" headers="*" secure="false"/> | |||
<site-control permitted-cross-domain-policies="all"/> | |||
<allow-access-from domain="*" to-ports="*" secure="false"/> | |||
<allow-http-request-headers-from domain="*" headers="*" secure="false"/> | |||
--> | |||
<!-- | |||
If you host a crossdomain.xml file with allow-access-from domain=“*” | |||
and don’t understand all of the points described here, you probably | |||
If you host a crossdomain.xml file with allow-access-from domain=“*” | |||
and don’t understand all of the points described here, you probably | |||
have a nasty security vulnerability. ~ simon willison | |||
--> | |||
@@ -109,6 +109,7 @@ HELPERS = File(JINJA2.child('helpers.html')) | |||
INDEX = File(JINJA2.child('index.html')) | |||
LAYOUT = File(JINJA2.child('layout.html')) | |||
LOGO = File(TEMPLATE_ROOT.child('../../../resources/hyde-logo.png')) | |||
XML = File(TEMPLATE_ROOT.child('../sites/test_jinja/content/crossdomain.xml')) | |||
def test_ancestors(): | |||
depth = 0 | |||
@@ -156,6 +157,7 @@ def test_mimetype(): | |||
def test_is_text(): | |||
assert HELPERS.is_text | |||
assert not LOGO.is_text | |||
assert XML.is_text | |||
def test_is_image(): | |||
assert not HELPERS.is_image | |||
@@ -62,7 +62,8 @@ class TestConfig(object): | |||
assert getattr(c, name) == root | |||
assert hasattr(c, path) | |||
assert getattr(c, path) == TEST_SITE_ROOT.child_folder(root) | |||
assert hasattr(c, 'plugins') | |||
assert len(c.plugins) == 0 | |||
assert c.deploy_root_path == TEST_SITE_ROOT.child_folder('deploy') | |||
def test_conf1(self): | |||
@@ -0,0 +1,246 @@ | |||
# -*- coding: utf-8 -*- | |||
""" | |||
Use nose | |||
`$ pip install nose` | |||
`$ nosetests` | |||
""" | |||
from hyde.exceptions import HydeException | |||
from hyde.fs import File, Folder | |||
from hyde.generator import Generator | |||
from hyde.plugin import Plugin | |||
from hyde.site import Site | |||
from mock import patch | |||
from nose.tools import raises, nottest, with_setup | |||
TEST_SITE = File(__file__).parent.child_folder('_test') | |||
class PluginLoaderStub(Plugin): | |||
pass | |||
class TestPlugins(object): | |||
@classmethod | |||
def setup_class(cls): | |||
TEST_SITE.make() | |||
TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) | |||
folders = [] | |||
text_files = [] | |||
binary_files = [] | |||
with TEST_SITE.child_folder('content').walker as walker: | |||
@walker.folder_visitor | |||
def visit_folder(folder): | |||
folders.append(folder.path) | |||
@walker.file_visitor | |||
def visit_file(afile): | |||
if not afile.is_text: | |||
binary_files.append(afile.path) | |||
else: | |||
text_files.append(afile.path) | |||
cls.content_nodes = sorted(folders) | |||
cls.content_text_resources = sorted(text_files) | |||
cls.content_binary_resources = sorted(binary_files) | |||
@classmethod | |||
def teardown_class(cls): | |||
TEST_SITE.delete() | |||
def setUp(self): | |||
self.site = Site(TEST_SITE) | |||
self.site.config.plugins = ['hyde.tests.test_plugin.PluginLoaderStub'] | |||
def test_can_load_plugin_modules(self): | |||
assert not len(self.site.plugins) | |||
Plugin.load_all(self.site) | |||
assert len(self.site.plugins) == 1 | |||
assert self.site.plugins[0].__class__.__name__ == 'PluginLoaderStub' | |||
def test_generator_loads_plugins(self): | |||
gen = Generator(self.site) | |||
assert len(self.site.plugins) == 1 | |||
def test_generator_template_registered_called(self): | |||
with patch.object(PluginLoaderStub, 'template_loaded') as template_loaded_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
assert template_loaded_stub.call_count == 1 | |||
def test_generator_template_begin_generation_called(self): | |||
with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
assert begin_generation_stub.call_count == 1 | |||
def test_generator_template_begin_generation_called_for_single_resource(self): | |||
with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder.child('about.html') | |||
gen.generate_resource_at_path(path) | |||
assert begin_generation_stub.call_count == 1 | |||
def test_generator_template_begin_generation_called_for_single_node(self): | |||
with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder | |||
gen.generate_node_at_path(path) | |||
assert begin_generation_stub.call_count == 1 | |||
def test_generator_template_generation_complete_called(self): | |||
with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
assert generation_complete_stub.call_count == 1 | |||
def test_generator_template_generation_complete_called_for_single_resource(self): | |||
with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder.child('about.html') | |||
gen.generate_resource_at_path(path) | |||
assert generation_complete_stub.call_count == 1 | |||
def test_generator_template_generation_complete_called_for_single_node(self): | |||
with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder | |||
gen.generate_node_at_path(path) | |||
assert generation_complete_stub.call_count == 1 | |||
def test_generator_template_begin_site_called(self): | |||
with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
assert begin_site_stub.call_count == 1 | |||
def test_generator_template_begin_site_not_called_for_single_resource(self): | |||
with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder.child('about.html') | |||
gen.generate_resource_at_path(path) | |||
assert begin_site_stub.call_count == 0 | |||
def test_generator_template_begin_site_not_called_for_single_node(self): | |||
with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder | |||
gen.generate_node_at_path(path) | |||
assert begin_site_stub.call_count == 0 | |||
def test_generator_template_site_complete_called(self): | |||
with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
assert site_complete_stub.call_count == 1 | |||
def test_generator_template_site_complete_not_called_for_single_resource(self): | |||
with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder.child('about.html') | |||
gen.generate_resource_at_path(path) | |||
assert site_complete_stub.call_count == 0 | |||
def test_generator_template_site_complete_not_called_for_single_node(self): | |||
with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder | |||
gen.generate_node_at_path(path) | |||
assert site_complete_stub.call_count == 0 | |||
def test_generator_template_begin_node_called(self): | |||
with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
assert begin_node_stub.call_count == len(self.content_nodes) | |||
called_with_nodes = sorted([arg[0][0].path for arg in begin_node_stub.call_args_list]) | |||
assert called_with_nodes == self.content_nodes | |||
def test_generator_template_begin_node_not_called_for_single_resource(self): | |||
with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: | |||
gen = Generator(self.site) | |||
gen.generate_resource_at_path(self.site.content.source_folder.child('about.html')) | |||
assert begin_node_stub.call_count == 0 | |||
def test_generator_template_node_complete_called(self): | |||
with patch.object(PluginLoaderStub, 'node_complete') as node_complete_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
assert node_complete_stub.call_count == len(self.content_nodes) | |||
called_with_nodes = sorted([arg[0][0].path for arg in node_complete_stub.call_args_list]) | |||
assert called_with_nodes == self.content_nodes | |||
def test_generator_template_node_complete_not_called_for_single_resource(self): | |||
with patch.object(PluginLoaderStub, 'node_complete') as node_complete_stub: | |||
gen = Generator(self.site) | |||
gen.generate_resource_at_path(self.site.content.source_folder.child('about.html')) | |||
assert node_complete_stub.call_count == 0 | |||
def test_generator_template_begin_text_resource_called(self): | |||
with patch.object(PluginLoaderStub, 'begin_text_resource') as begin_text_resource_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
called_with_resources = sorted([arg[0][0].path for arg in begin_text_resource_stub.call_args_list]) | |||
assert begin_text_resource_stub.call_count == len(self.content_text_resources) | |||
assert called_with_resources == self.content_text_resources | |||
def test_generator_template_begin_text_resource_called_for_single_resource(self): | |||
with patch.object(PluginLoaderStub, 'begin_text_resource') as begin_text_resource_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder.child('about.html') | |||
gen.generate_resource_at_path(path) | |||
called_with_resources = sorted([arg[0][0].path for arg in begin_text_resource_stub.call_args_list]) | |||
assert begin_text_resource_stub.call_count == 1 | |||
assert called_with_resources[0] == path | |||
def test_generator_template_begin_binary_resource_called(self): | |||
with patch.object(PluginLoaderStub, 'begin_binary_resource') as begin_binary_resource_stub: | |||
gen = Generator(self.site) | |||
gen.generate_all() | |||
called_with_resources = sorted([arg[0][0].path for arg in begin_binary_resource_stub.call_args_list]) | |||
assert begin_binary_resource_stub.call_count == len(self.content_binary_resources) | |||
assert called_with_resources == self.content_binary_resources | |||
def test_generator_template_begin_binary_resource_called_for_single_resource(self): | |||
with patch.object(PluginLoaderStub, 'begin_binary_resource') as begin_binary_resource_stub: | |||
gen = Generator(self.site) | |||
path = self.site.content.source_folder.child('favicon.ico') | |||
gen.generate_resource_at_path(path) | |||
called_with_resources = sorted([arg[0][0].path for arg in begin_binary_resource_stub.call_args_list]) | |||
assert begin_binary_resource_stub.call_count == 1 | |||
assert called_with_resources[0] == path |
@@ -75,7 +75,8 @@ def test_walk_resources(): | |||
"merry-christmas.html", | |||
"crossdomain.xml", | |||
"favicon.ico", | |||
"robots.txt" | |||
"robots.txt", | |||
"site.css" | |||
] | |||
pages.sort() | |||
expected.sort() | |||