* Remove `hyde.fs` use `fswrap` package instead. * Remove logging functions from `hyde.util`. Use `commando.util` instead. * Remove `hyde.loader`. Use `commando.util.load_python_object` instead.main
@@ -2,6 +2,9 @@ Version 0.8.5a16 | |||||
============================================================ | ============================================================ | ||||
* Upgrade dependencies and setup for 0.8.5 | * Upgrade dependencies and setup for 0.8.5 | ||||
* Remove `hyde.fs` use `fswrap` package instead. | |||||
* Remove logging functions from `hyde.util`. Use `commando.util` instead. | |||||
* Remove `hyde.loader`. Use `commando.util.load_python_object` instead. | |||||
* Bug fix: Use the released version of typogrify. (Issue #193) | * Bug fix: Use the released version of typogrify. (Issue #193) | ||||
Version 0.8.5a15 | Version 0.8.5a15 | ||||
@@ -2,6 +2,12 @@ | |||||
""" | """ | ||||
Implements the hyde entry point commands | Implements the hyde entry point commands | ||||
""" | """ | ||||
from hyde.exceptions import HydeException | |||||
from hyde.layout import Layout, HYDE_DATA | |||||
from hyde.model import Config | |||||
from hyde.site import Site | |||||
from hyde.version import __version__ | |||||
from commando import ( | from commando import ( | ||||
Application, | Application, | ||||
command, | command, | ||||
@@ -10,19 +16,21 @@ from commando import ( | |||||
true, | true, | ||||
version | version | ||||
) | ) | ||||
from hyde.exceptions import HydeException | |||||
from hyde.fs import FS, Folder | |||||
from hyde.layout import Layout, HYDE_DATA | |||||
from hyde.model import Config | |||||
from hyde.site import Site | |||||
from hyde.version import __version__ | |||||
from hyde.util import getLoggerWithConsoleHandler | |||||
from commando.util import getLoggerWithConsoleHandler | |||||
from fswrap import FS, Folder | |||||
HYDE_LAYOUTS = "HYDE_LAYOUTS" | HYDE_LAYOUTS = "HYDE_LAYOUTS" | ||||
class Engine(Application): | class Engine(Application): | ||||
def __init__(self, raise_exceptions=True): | |||||
logger = getLoggerWithConsoleHandler('hyde') | |||||
super(Engine, self).__init__( | |||||
raise_exceptions=raise_exceptions, | |||||
logger=logger | |||||
) | |||||
@command(description='hyde - a python static website generator', | @command(description='hyde - a python static website generator', | ||||
epilog='Use %(prog)s {command} -h to get help on individual commands') | epilog='Use %(prog)s {command} -h to get help on individual commands') | ||||
@true('-v', '--verbose', help="Show detailed information in console") | @true('-v', '--verbose', help="Show detailed information in console") | ||||
@@ -34,10 +42,6 @@ class Engine(Application): | |||||
to provide common parameters for the subcommands and some generic stuff | to provide common parameters for the subcommands and some generic stuff | ||||
like version and metadata | like version and metadata | ||||
""" | """ | ||||
if args.verbose: | |||||
import logging | |||||
self.logger.setLevel(logging.DEBUG) | |||||
sitepath = Folder(args.sitepath).fully_expanded_path | sitepath = Folder(args.sitepath).fully_expanded_path | ||||
return Folder(sitepath) | return Folder(sitepath) | ||||
@@ -4,7 +4,8 @@ Plugins related to folders and paths | |||||
""" | """ | ||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.fs import Folder | |||||
from fswrap import Folder | |||||
class FlattenerPlugin(Plugin): | class FlattenerPlugin(Plugin): | ||||
""" | """ | ||||
@@ -6,7 +6,6 @@ Contains classes and utilities to extract information from git repository | |||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
import subprocess | import subprocess | ||||
import traceback | |||||
from dateutil.parser import parse | from dateutil.parser import parse | ||||
class GitDatesPlugin(Plugin): | class GitDatesPlugin(Plugin): | ||||
@@ -3,16 +3,12 @@ | |||||
Contains classes and utilities related to grouping | Contains classes and utilities related to grouping | ||||
resources and nodes in hyde. | resources and nodes in hyde. | ||||
""" | """ | ||||
import re | |||||
from hyde.model import Expando | from hyde.model import Expando | ||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.site import Node, Resource | from hyde.site import Node, Resource | ||||
from hyde.util import add_method, add_property, pairwalk | from hyde.util import add_method, add_property, pairwalk | ||||
from collections import namedtuple | from collections import namedtuple | ||||
from functools import partial | |||||
from itertools import ifilter, izip, tee, product | |||||
from operator import attrgetter | |||||
Grouper = namedtuple('Grouper', 'group resources') | Grouper = namedtuple('Grouper', 'group resources') | ||||
@@ -4,7 +4,8 @@ jpegoptim plugin | |||||
""" | """ | ||||
from hyde.plugin import CLTransformer | from hyde.plugin import CLTransformer | ||||
from hyde.fs import File | |||||
from fswrap import File | |||||
class JPEGOptimPlugin(CLTransformer): | class JPEGOptimPlugin(CLTransformer): | ||||
""" | """ | ||||
@@ -4,11 +4,11 @@ Less css plugin | |||||
""" | """ | ||||
from hyde.plugin import CLTransformer | from hyde.plugin import CLTransformer | ||||
from hyde.fs import File | |||||
import re | import re | ||||
import subprocess | import subprocess | ||||
from fswrap import File | |||||
class LessCSSPlugin(CLTransformer): | class LessCSSPlugin(CLTransformer): | ||||
""" | """ | ||||
@@ -4,7 +4,8 @@ OPTIPNG plugin | |||||
""" | """ | ||||
from hyde.plugin import CLTransformer | from hyde.plugin import CLTransformer | ||||
from hyde.fs import File | |||||
from fswrap import File | |||||
class OptiPNGPlugin(CLTransformer): | class OptiPNGPlugin(CLTransformer): | ||||
""" | """ | ||||
@@ -5,11 +5,12 @@ each page to a copy of the original resource. | |||||
""" | """ | ||||
import os | import os | ||||
from hyde.fs import File | |||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.site import Resource | from hyde.site import Resource | ||||
from hyde.util import pairwalk | from hyde.util import pairwalk | ||||
from fswrap import File | |||||
class Page: | class Page: | ||||
def __init__(self, posts, number): | def __init__(self, posts, number): | ||||
self.posts = posts | self.posts = posts | ||||
@@ -3,8 +3,6 @@ | |||||
Contains classes and utilities related to sorting | Contains classes and utilities related to sorting | ||||
resources and nodes in hyde. | resources and nodes in hyde. | ||||
""" | """ | ||||
import re | |||||
from hyde.model import Expando | |||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.site import Node, Resource | from hyde.site import Node, Resource | ||||
from hyde.util import add_method, pairwalk | from hyde.util import add_method, pairwalk | ||||
@@ -40,7 +38,7 @@ def attributes_checker(item, attributes=None): | |||||
Checks if the given list of attributes exist. | Checks if the given list of attributes exist. | ||||
""" | """ | ||||
try: | try: | ||||
x = attrgetter(*attributes)(item) | |||||
attrgetter(*attributes)(item) | |||||
return True | return True | ||||
except AttributeError: | except AttributeError: | ||||
return False | return False | ||||
@@ -41,16 +41,16 @@ hyde templating workflow. You would end up with:: | |||||
from __future__ import absolute_import | from __future__ import absolute_import | ||||
import os | import os | ||||
import sys | |||||
import json | import json | ||||
import tempfile | |||||
import tempfile | |||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.fs import File, Folder | |||||
from hyde.model import Expando | from hyde.model import Expando | ||||
from hyde.ext.plugins.meta import MetaPlugin as _MetaPlugin | from hyde.ext.plugins.meta import MetaPlugin as _MetaPlugin | ||||
from hyde.util import getLoggerWithNullHandler | |||||
from commado.util import getLoggerWithNullHandler | |||||
from fswrap import File, Folder | |||||
logger = getLoggerWithNullHandler('hyde.ext.plugins.sphinx') | logger = getLoggerWithNullHandler('hyde.ext.plugins.sphinx') | ||||
try: | try: | ||||
@@ -101,7 +101,7 @@ class SphinxPlugin(Plugin): | |||||
def sphinx_config(self): | def sphinx_config(self): | ||||
"""Configuration options for sphinx. | """Configuration options for sphinx. | ||||
This is a lazily-generated property giving the options from the | |||||
This is a lazily-generated property giving the options from the | |||||
sphinx configuration file. It's generated by actualy executing | sphinx configuration file. It's generated by actualy executing | ||||
the config file, so don't do anything silly in there. | the config file, so don't do anything silly in there. | ||||
""" | """ | ||||
@@ -276,7 +276,7 @@ class HydeJSONHTMLBuilder(JSONHTMLBuilder): | |||||
This is a Sphinx builder that serilises the generated HTML fragments into | This is a Sphinx builder that serilises the generated HTML fragments into | ||||
a JSON docuent, so they can be later retrieved and dealt with at will. | a JSON docuent, so they can be later retrieved and dealt with at will. | ||||
The only customistion we do over the standard JSONHTMLBuilder is to | |||||
The only customistion we do over the standard JSONHTMLBuilder is to | |||||
reference documents with a .html suffix, so that internal link will | reference documents with a .html suffix, so that internal link will | ||||
work correctly once things have been processed by Hyde. | work correctly once things have been processed by Hyde. | ||||
""" | """ | ||||
@@ -4,11 +4,11 @@ Less css plugin | |||||
""" | """ | ||||
from hyde.plugin import CLTransformer | from hyde.plugin import CLTransformer | ||||
from hyde.fs import File | |||||
import re | import re | ||||
import subprocess | import subprocess | ||||
from fswrap import File | |||||
class StylusPlugin(CLTransformer): | class StylusPlugin(CLTransformer): | ||||
""" | """ | ||||
@@ -3,19 +3,15 @@ | |||||
Contains classes and utilities related to tagging | Contains classes and utilities related to tagging | ||||
resources in hyde. | resources in hyde. | ||||
""" | """ | ||||
import re | |||||
from hyde.fs import File, Folder | |||||
from hyde.model import Expando | from hyde.model import Expando | ||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.site import Node, Resource | |||||
from hyde.util import add_method, add_property, pairwalk | |||||
from hyde.site import Node | |||||
from hyde.util import add_method | |||||
from collections import namedtuple | |||||
from datetime import datetime | |||||
from functools import partial | |||||
from itertools import ifilter, izip, tee, product | |||||
from operator import attrgetter | from operator import attrgetter | ||||
from fswrap import File, Folder | |||||
class Tag(Expando): | class Tag(Expando): | ||||
""" | """ | ||||
@@ -4,7 +4,8 @@ Uglify plugin | |||||
""" | """ | ||||
from hyde.plugin import CLTransformer | from hyde.plugin import CLTransformer | ||||
from hyde.fs import File | |||||
from fswrap import File | |||||
class UglifyPlugin(CLTransformer): | class UglifyPlugin(CLTransformer): | ||||
""" | """ | ||||
@@ -2,14 +2,12 @@ | |||||
""" | """ | ||||
Contains classes and utilities related to hyde urls. | Contains classes and utilities related to hyde urls. | ||||
""" | """ | ||||
import re | |||||
from hyde.fs import File, Folder | |||||
from hyde.model import Expando | |||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.site import Site, Node, Resource | |||||
from hyde.site import Site | |||||
from functools import wraps | from functools import wraps | ||||
from fswrap import File | |||||
class UrlCleanerPlugin(Plugin): | class UrlCleanerPlugin(Plugin): | ||||
""" | """ | ||||
@@ -3,12 +3,9 @@ Contains classes and utilities that help publishing a hyde website to | |||||
distributed version control systems. | distributed version control systems. | ||||
""" | """ | ||||
import sys | |||||
import abc | |||||
from hyde.fs import File, Folder | |||||
from hyde.publisher import Publisher | from hyde.publisher import Publisher | ||||
import abc | |||||
from subprocess import Popen, PIPE | from subprocess import Popen, PIPE | ||||
class DVCS(Publisher): | class DVCS(Publisher): | ||||
@@ -14,10 +14,11 @@ are valid URLs that can be used with this publisher: | |||||
import getpass | import getpass | ||||
import hashlib | import hashlib | ||||
from hyde.fs import File, Folder | |||||
from hyde.publisher import Publisher | from hyde.publisher import Publisher | ||||
from hyde.util import getLoggerWithNullHandler | |||||
from commando.util import getLoggerWithNullHandler | |||||
logger = getLoggerWithNullHandler('hyde.ext.publishers.pyfs') | logger = getLoggerWithNullHandler('hyde.ext.publishers.pyfs') | ||||
@@ -13,10 +13,9 @@ import urlparse | |||||
from base64 import standard_b64encode | from base64 import standard_b64encode | ||||
import ConfigParser | import ConfigParser | ||||
from hyde.fs import File, Folder | |||||
from hyde.publisher import Publisher | from hyde.publisher import Publisher | ||||
from hyde.util import getLoggerWithNullHandler | |||||
from commando.util import getLoggerWithNullHandler | |||||
logger = getLoggerWithNullHandler('hyde.ext.publishers.pypi') | logger = getLoggerWithNullHandler('hyde.ext.publishers.pypi') | ||||
@@ -136,5 +135,5 @@ class PyPI(Publisher): | |||||
con.close() | con.close() | ||||
finally: | finally: | ||||
tf.close() | tf.close() | ||||
@@ -11,15 +11,20 @@ from urllib import quote, unquote | |||||
from hyde.model import Expando | from hyde.model import Expando | ||||
from hyde.template import HtmlWrap, Template | from hyde.template import HtmlWrap, Template | ||||
from hyde.util import getLoggerWithNullHandler | |||||
from operator import attrgetter | from operator import attrgetter | ||||
from jinja2 import contextfunction, Environment | |||||
from jinja2 import FileSystemLoader, FileSystemBytecodeCache | |||||
from jinja2 import ( | |||||
contextfunction, | |||||
Environment, | |||||
FileSystemLoader, | |||||
FileSystemBytecodeCache | |||||
) | |||||
from jinja2 import contextfilter, environmentfilter, Markup, Undefined, nodes | from jinja2 import contextfilter, environmentfilter, Markup, Undefined, nodes | ||||
from jinja2.ext import Extension | from jinja2.ext import Extension | ||||
from jinja2.exceptions import TemplateError | from jinja2.exceptions import TemplateError | ||||
from commando.util import getLoggerWithNullHandler | |||||
logger = getLoggerWithNullHandler('hyde.engine.Jinja2') | logger = getLoggerWithNullHandler('hyde.engine.Jinja2') | ||||
class SilentUndefined(Undefined): | class SilentUndefined(Undefined): | ||||
@@ -1,618 +0,0 @@ | |||||
# -*- coding: utf-8 -*- | |||||
""" | |||||
Unified object oriented interface for interacting with file system objects. | |||||
File system operations in python are distributed across modules: os, os.path, | |||||
fnamtch, shutil and distutils. This module attempts to make the right choices | |||||
for common operations to provide a single interface. | |||||
""" | |||||
import codecs | |||||
from datetime import datetime | |||||
import mimetypes | |||||
import os | |||||
import shutil | |||||
from distutils import dir_util | |||||
import functools | |||||
import fnmatch | |||||
from hyde.util import getLoggerWithNullHandler | |||||
logger = getLoggerWithNullHandler('fs') | |||||
# pylint: disable-msg=E0611 | |||||
__all__ = ['File', 'Folder'] | |||||
class FS(object): | |||||
""" | |||||
The base file system object | |||||
""" | |||||
def __init__(self, path): | |||||
super(FS, self).__init__() | |||||
if path == os.sep: | |||||
self.path = path | |||||
else: | |||||
self.path = os.path.expandvars(os.path.expanduser( | |||||
unicode(path).strip().rstrip(os.sep))) | |||||
def __str__(self): | |||||
return self.path | |||||
def __repr__(self): | |||||
return self.path | |||||
def __eq__(self, other): | |||||
return unicode(self) == unicode(other) | |||||
def __ne__(self, other): | |||||
return unicode(self) != unicode(other) | |||||
@property | |||||
def fully_expanded_path(self): | |||||
""" | |||||
Returns the absolutely absolute path. Calls os.( | |||||
normpath, normcase, expandvars and expanduser). | |||||
""" | |||||
return os.path.abspath( | |||||
os.path.normpath( | |||||
os.path.normcase( | |||||
os.path.expandvars( | |||||
os.path.expanduser(self.path))))) | |||||
@property | |||||
def exists(self): | |||||
""" | |||||
Does the file system object exist? | |||||
""" | |||||
return os.path.exists(self.path) | |||||
@property | |||||
def name(self): | |||||
""" | |||||
Returns the name of the FS object with its extension | |||||
""" | |||||
return os.path.basename(self.path) | |||||
@property | |||||
def parent(self): | |||||
""" | |||||
The parent folder. Returns a `Folder` object. | |||||
""" | |||||
return Folder(os.path.dirname(self.path)) | |||||
@property | |||||
def depth(self): | |||||
""" | |||||
Returns the number of ancestors of this directory. | |||||
""" | |||||
return len(self.path.rstrip(os.sep).split(os.sep)) | |||||
def ancestors(self, stop=None): | |||||
""" | |||||
Generates the parents until stop or the absolute | |||||
root directory is reached. | |||||
""" | |||||
folder = self | |||||
while folder.parent != stop: | |||||
if folder.parent == folder: | |||||
return | |||||
yield folder.parent | |||||
folder = folder.parent | |||||
def is_descendant_of(self, ancestor): | |||||
""" | |||||
Checks if this folder is inside the given ancestor. | |||||
""" | |||||
stop = Folder(ancestor) | |||||
for folder in self.ancestors(): | |||||
if folder == stop: | |||||
return True | |||||
if stop.depth > folder.depth: | |||||
return False | |||||
return False | |||||
def get_relative_path(self, root): | |||||
""" | |||||
Gets the fragment of the current path starting at root. | |||||
""" | |||||
if self.path == root: | |||||
return '' | |||||
ancestors = self.ancestors(stop=root) | |||||
return functools.reduce(lambda f, p: Folder(p.name).child(f), | |||||
ancestors, | |||||
self.name) | |||||
def get_mirror(self, target_root, source_root=None): | |||||
""" | |||||
Returns a File or Folder object that reperesents if the entire | |||||
fragment of this directory starting with `source_root` were copied | |||||
to `target_root`. | |||||
>>> Folder('/usr/local/hyde/stuff').get_mirror('/usr/tmp', | |||||
source_root='/usr/local/hyde') | |||||
Folder('/usr/tmp/stuff') | |||||
""" | |||||
fragment = self.get_relative_path( | |||||
source_root if source_root else self.parent) | |||||
return Folder(target_root).child(fragment) | |||||
@staticmethod | |||||
def file_or_folder(path): | |||||
""" | |||||
Returns a File or Folder object that would represent the given path. | |||||
""" | |||||
target = unicode(path) | |||||
return Folder(target) if os.path.isdir(target) else File(target) | |||||
def __get_destination__(self, destination): | |||||
""" | |||||
Returns a File or Folder object that would represent this entity | |||||
if it were copied or moved to `destination`. | |||||
""" | |||||
if isinstance(destination, File) or os.path.isfile(unicode(destination)): | |||||
return destination | |||||
else: | |||||
return FS.file_or_folder(Folder(destination).child(self.name)) | |||||
class File(FS): | |||||
""" | |||||
The File object. | |||||
""" | |||||
def __init__(self, path): | |||||
super(File, self).__init__(path) | |||||
@property | |||||
def name_without_extension(self): | |||||
""" | |||||
Returns the name of the FS object without its extension | |||||
""" | |||||
return os.path.splitext(self.name)[0] | |||||
@property | |||||
def extension(self): | |||||
""" | |||||
File extension prefixed with a dot. | |||||
""" | |||||
return os.path.splitext(self.path)[1] | |||||
@property | |||||
def kind(self): | |||||
""" | |||||
File extension without dot prefix. | |||||
""" | |||||
return self.extension.lstrip(".") | |||||
@property | |||||
def size(self): | |||||
""" | |||||
Size of this file. | |||||
""" | |||||
if not self.exists: | |||||
return -1 | |||||
return os.path.getsize(self.path) | |||||
@property | |||||
def mimetype(self): | |||||
""" | |||||
Gets the mimetype of this file. | |||||
""" | |||||
(mime, _) = 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 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" | |||||
@property | |||||
def last_modified(self): | |||||
""" | |||||
Returns a datetime object representing the last modified time. | |||||
Calls os.path.getmtime. | |||||
""" | |||||
return datetime.fromtimestamp(os.path.getmtime(self.path)) | |||||
def has_changed_since(self, basetime): | |||||
""" | |||||
Returns True if the file has been changed since the given time. | |||||
""" | |||||
return self.last_modified > basetime | |||||
def older_than(self, another_file): | |||||
""" | |||||
Checks if this file is older than the given file. Uses last_modified to | |||||
determine age. | |||||
""" | |||||
return self.last_modified < File(unicode(another_file)).last_modified | |||||
@staticmethod | |||||
def make_temp(text): | |||||
""" | |||||
Creates a temprorary file and writes the `text` into it | |||||
""" | |||||
import tempfile | |||||
(handle, path) = tempfile.mkstemp(text=True) | |||||
os.close(handle) | |||||
afile = File(path) | |||||
afile.write(text) | |||||
return afile | |||||
def read_all(self, encoding='utf-8'): | |||||
""" | |||||
Reads from the file and returns the content as a string. | |||||
""" | |||||
logger.info("Reading everything from %s" % self) | |||||
with codecs.open(self.path, 'r', encoding) as fin: | |||||
read_text = fin.read() | |||||
return read_text | |||||
def write(self, text, encoding="utf-8"): | |||||
""" | |||||
Writes the given text to the file using the given encoding. | |||||
""" | |||||
logger.info("Writing to %s" % self) | |||||
with codecs.open(self.path, 'w', encoding) as fout: | |||||
fout.write(text) | |||||
def copy_to(self, destination): | |||||
""" | |||||
Copies the file to the given destination. Returns a File | |||||
object that represents the target file. `destination` must | |||||
be a File or Folder object. | |||||
""" | |||||
target = self.__get_destination__(destination) | |||||
logger.info("Copying %s to %s" % (self, target)) | |||||
shutil.copy(self.path, unicode(destination)) | |||||
return target | |||||
def delete(self): | |||||
""" | |||||
Delete the file if it exists. | |||||
""" | |||||
if self.exists: | |||||
os.remove(self.path) | |||||
class FSVisitor(object): | |||||
""" | |||||
Implements syntactic sugar for walking and listing folders | |||||
""" | |||||
def __init__(self, folder, pattern=None): | |||||
super(FSVisitor, self).__init__() | |||||
self.folder = folder | |||||
self.pattern = pattern | |||||
def folder_visitor(self, function): | |||||
""" | |||||
Decorator for `visit_folder` protocol | |||||
""" | |||||
self.visit_folder = function | |||||
return function | |||||
def file_visitor(self, function): | |||||
""" | |||||
Decorator for `visit_file` protocol | |||||
""" | |||||
self.visit_file = function | |||||
return function | |||||
def finalizer(self, function): | |||||
""" | |||||
Decorator for `visit_complete` protocol | |||||
""" | |||||
self.visit_complete = function | |||||
return function | |||||
def __enter__(self): | |||||
return self | |||||
def __exit__(self, exc_type, exc_val, exc_tb): | |||||
pass | |||||
class FolderWalker(FSVisitor): | |||||
""" | |||||
Walks the entire hirearchy of this directory starting with itself. | |||||
If a pattern is provided, only the files that match the pattern are | |||||
processed. | |||||
""" | |||||
def walk(self, walk_folders=False, walk_files=False): | |||||
""" | |||||
A simple generator that yields a File or Folder object based on | |||||
the arguments. | |||||
""" | |||||
if not walk_files and not walk_folders: | |||||
return | |||||
for root, _, a_files in os.walk(self.folder.path, followlinks=True): | |||||
folder = Folder(root) | |||||
if walk_folders: | |||||
yield folder | |||||
if walk_files: | |||||
for a_file in a_files: | |||||
if (not self.pattern or | |||||
fnmatch.fnmatch(a_file, self.pattern)): | |||||
yield File(folder.child(a_file)) | |||||
def walk_all(self): | |||||
""" | |||||
Yield both Files and Folders as the tree is walked. | |||||
""" | |||||
return self.walk(walk_folders=True, walk_files=True) | |||||
def walk_files(self): | |||||
""" | |||||
Yield only Files. | |||||
""" | |||||
return self.walk(walk_folders=False, walk_files=True) | |||||
def walk_folders(self): | |||||
""" | |||||
Yield only Folders. | |||||
""" | |||||
return self.walk(walk_folders=True, walk_files=False) | |||||
def __exit__(self, exc_type, exc_val, exc_tb): | |||||
""" | |||||
Automatically walk the folder when the context manager is exited. | |||||
Calls self.visit_folder first and then calls self.visit_file for | |||||
any files found. After all files and folders have been exhausted | |||||
self.visit_complete is called. | |||||
If visitor.visit_folder returns False, the files in the folder are not | |||||
processed. | |||||
""" | |||||
def __visit_folder__(folder): | |||||
process_folder = True | |||||
if hasattr(self, 'visit_folder'): | |||||
process_folder = self.visit_folder(folder) | |||||
# If there is no return value assume true | |||||
# | |||||
if process_folder is None: | |||||
process_folder = True | |||||
return process_folder | |||||
def __visit_file__(a_file): | |||||
if hasattr(self, 'visit_file'): | |||||
self.visit_file(a_file) | |||||
def __visit_complete__(): | |||||
if hasattr(self, 'visit_complete'): | |||||
self.visit_complete() | |||||
for root, dirs, a_files in os.walk(self.folder.path, followlinks=True): | |||||
folder = Folder(root) | |||||
if not __visit_folder__(folder): | |||||
dirs[:] = [] | |||||
continue | |||||
for a_file in a_files: | |||||
if not self.pattern or fnmatch.fnmatch(a_file, self.pattern): | |||||
__visit_file__(File(folder.child(a_file))) | |||||
__visit_complete__() | |||||
class FolderLister(FSVisitor): | |||||
""" | |||||
Lists the contents of this directory. | |||||
If a pattern is provided, only the files that match the pattern are | |||||
processed. | |||||
""" | |||||
def list(self, list_folders=False, list_files=False): | |||||
""" | |||||
A simple generator that yields a File or Folder object based on | |||||
the arguments. | |||||
""" | |||||
a_files = os.listdir(self.folder.path) | |||||
for a_file in a_files: | |||||
path = self.folder.child(a_file) | |||||
if os.path.isdir(path): | |||||
if list_folders: | |||||
yield Folder(path) | |||||
elif list_files: | |||||
if not self.pattern or fnmatch.fnmatch(a_file, self.pattern): | |||||
yield File(path) | |||||
def list_all(self): | |||||
""" | |||||
Yield both Files and Folders as the folder is listed. | |||||
""" | |||||
return self.list(list_folders=True, list_files=True) | |||||
def list_files(self): | |||||
""" | |||||
Yield only Files. | |||||
""" | |||||
return self.list(list_folders=False, list_files=True) | |||||
def list_folders(self): | |||||
""" | |||||
Yield only Folders. | |||||
""" | |||||
return self.list(list_folders=True, list_files=False) | |||||
def __exit__(self, exc_type, exc_val, exc_tb): | |||||
""" | |||||
Automatically list the folder contents when the context manager | |||||
is exited. | |||||
Calls self.visit_folder first and then calls self.visit_file for | |||||
any files found. After all files and folders have been exhausted | |||||
self.visit_complete is called. | |||||
""" | |||||
a_files = os.listdir(self.folder.path) | |||||
for a_file in a_files: | |||||
path = self.folder.child(a_file) | |||||
if os.path.isdir(path) and hasattr(self, 'visit_folder'): | |||||
self.visit_folder(Folder(path)) | |||||
elif hasattr(self, 'visit_file'): | |||||
if not self.pattern or fnmatch.fnmatch(a_file, self.pattern): | |||||
self.visit_file(File(path)) | |||||
if hasattr(self, 'visit_complete'): | |||||
self.visit_complete() | |||||
class Folder(FS): | |||||
""" | |||||
Represents a directory. | |||||
""" | |||||
def __init__(self, path): | |||||
super(Folder, self).__init__(path) | |||||
def child_folder(self, fragment): | |||||
""" | |||||
Returns a folder object by combining the fragment to this folder's path | |||||
""" | |||||
return Folder(os.path.join(self.path, Folder(fragment).path)) | |||||
def child(self, fragment): | |||||
""" | |||||
Returns a path of a child item represented by `fragment`. | |||||
""" | |||||
return os.path.join(self.path, FS(fragment).path) | |||||
def make(self): | |||||
""" | |||||
Creates this directory and any of the missing directories in the path. | |||||
Any errors that may occur are eaten. | |||||
""" | |||||
try: | |||||
if not self.exists: | |||||
logger.info("Creating %s" % self.path) | |||||
os.makedirs(self.path) | |||||
except os.error: | |||||
pass | |||||
return self | |||||
def delete(self): | |||||
""" | |||||
Deletes the directory if it exists. | |||||
""" | |||||
if self.exists: | |||||
logger.info("Deleting %s" % self.path) | |||||
shutil.rmtree(self.path) | |||||
def copy_to(self, destination): | |||||
""" | |||||
Copies this directory to the given destination. Returns a Folder object | |||||
that represents the moved directory. | |||||
""" | |||||
target = self.__get_destination__(destination) | |||||
logger.info("Copying %s to %s" % (self, target)) | |||||
shutil.copytree(self.path, unicode(target)) | |||||
return target | |||||
def move_to(self, destination): | |||||
""" | |||||
Moves this directory to the given destination. Returns a Folder object | |||||
that represents the moved directory. | |||||
""" | |||||
target = self.__get_destination__(destination) | |||||
logger.info("Move %s to %s" % (self, target)) | |||||
shutil.move(self.path, unicode(target)) | |||||
return target | |||||
def rename_to(self, destination_name): | |||||
""" | |||||
Moves this directory to the given destination. Returns a Folder object | |||||
that represents the moved directory. | |||||
""" | |||||
target = self.parent.child_folder(destination_name) | |||||
logger.info("Rename %s to %s" % (self, target)) | |||||
shutil.move(self.path, unicode(target)) | |||||
return target | |||||
def _create_target_tree(self, target): | |||||
""" | |||||
There is a bug in dir_util that makes `copy_tree` crash if a folder in | |||||
the tree has been deleted before and readded now. To workaround the | |||||
bug, we first walk the tree and create directories that are needed. | |||||
""" | |||||
source = self | |||||
with source.walker as walker: | |||||
@walker.folder_visitor | |||||
def visit_folder(folder): | |||||
""" | |||||
Create the mirror directory | |||||
""" | |||||
if folder != source: | |||||
Folder(folder.get_mirror(target, source)).make() | |||||
def copy_contents_to(self, destination): | |||||
""" | |||||
Copies the contents of this directory to the given destination. | |||||
Returns a Folder object that represents the moved directory. | |||||
""" | |||||
logger.info("Copying contents of %s to %s" % (self, destination)) | |||||
target = Folder(destination) | |||||
target.make() | |||||
self._create_target_tree(target) | |||||
dir_util.copy_tree(self.path, unicode(target)) | |||||
return target | |||||
def get_walker(self, pattern=None): | |||||
""" | |||||
Return a `FolderWalker` object with a set pattern. | |||||
""" | |||||
return FolderWalker(self, pattern) | |||||
@property | |||||
def walker(self): | |||||
""" | |||||
Return a `FolderWalker` object | |||||
""" | |||||
return FolderWalker(self) | |||||
def get_lister(self, pattern=None): | |||||
""" | |||||
Return a `FolderLister` object with a set pattern. | |||||
""" | |||||
return FolderLister(self, pattern) | |||||
@property | |||||
def lister(self): | |||||
""" | |||||
Return a `FolderLister` object | |||||
""" | |||||
return FolderLister(self) |
@@ -4,17 +4,17 @@ The generator class and related utility functions. | |||||
""" | """ | ||||
from hyde.exceptions import HydeException | from hyde.exceptions import HydeException | ||||
from hyde.fs import File, Folder | |||||
from fswrap import File, Folder | |||||
from hyde.model import Context, Dependents | from hyde.model import Context, Dependents | ||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.template import Template | from hyde.template import Template | ||||
from hyde.site import Node, Resource | |||||
from hyde.site import Resource | |||||
from contextlib import contextmanager | from contextlib import contextmanager | ||||
from datetime import datetime | from datetime import datetime | ||||
from shutil import copymode | from shutil import copymode | ||||
from hyde.util import getLoggerWithNullHandler | |||||
from commando.util import getLoggerWithNullHandler | |||||
logger = getLoggerWithNullHandler('hyde.engine') | logger = getLoggerWithNullHandler('hyde.engine') | ||||
@@ -4,7 +4,7 @@ Classes, functions and utilties related to hyde layouts | |||||
""" | """ | ||||
import os | import os | ||||
from hyde.fs import File, Folder | |||||
from fswrap import File, Folder | |||||
HYDE_DATA = "HYDE_DATA" | HYDE_DATA = "HYDE_DATA" | ||||
LAYOUTS = "layouts" | LAYOUTS = "layouts" | ||||
@@ -1,49 +0,0 @@ | |||||
# -*- coding: utf-8 -*- | |||||
""" | |||||
Generic loader of extensions (plugins & templates) | |||||
""" | |||||
import sys | |||||
from hyde.exceptions import HydeException | |||||
from hyde.util import getLoggerWithNullHandler | |||||
logger = getLoggerWithNullHandler('hyde.engine') | |||||
plugins = {} | |||||
templates = {} | |||||
def load_python_object(name): | |||||
""" | |||||
Loads a python module from string | |||||
""" | |||||
(module_name, _, object_name) = name.rpartition(".") | |||||
if module_name == '': | |||||
(module_name, object_name) = (object_name, module_name) | |||||
try: | |||||
logger.debug('Loading module [%s]' % module_name) | |||||
module = __import__(module_name) | |||||
except ImportError: | |||||
raise HydeException("The given module name [%s] is invalid." % | |||||
module_name) | |||||
if object_name == '': | |||||
return module | |||||
try: | |||||
module = sys.modules[module_name] | |||||
except KeyError: | |||||
raise HydeException("Error occured when loading module [%s]" % | |||||
module_name) | |||||
try: | |||||
logger.debug('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 " | |||||
"configuration or ensure that the module is " | |||||
"installed properly" % | |||||
(name, module_name, object_name)) |
@@ -2,14 +2,14 @@ | |||||
""" | """ | ||||
Contains data structures and utilities for hyde. | Contains data structures and utilities for hyde. | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
import codecs | import codecs | ||||
import yaml | import yaml | ||||
from datetime import datetime | from datetime import datetime | ||||
from UserDict import IterableUserDict | from UserDict import IterableUserDict | ||||
from hyde.util import getLoggerWithNullHandler | |||||
from commando.util import getLoggerWithNullHandler | |||||
from fswrap import File, Folder | |||||
logger = getLoggerWithNullHandler('hyde.engine') | logger = getLoggerWithNullHandler('hyde.engine') | ||||
class Expando(object): | class Expando(object): | ||||
@@ -2,23 +2,21 @@ | |||||
""" | """ | ||||
Contains definition for a plugin protocol and other utiltities. | Contains definition for a plugin protocol and other utiltities. | ||||
""" | """ | ||||
import abc | |||||
from hyde import loader | |||||
from hyde.exceptions import HydeException | from hyde.exceptions import HydeException | ||||
from hyde.fs import File | |||||
from hyde.util import getLoggerWithNullHandler, first_match, discover_executable | |||||
from hyde.util import first_match, discover_executable | |||||
from hyde.model import Expando | from hyde.model import Expando | ||||
import abc | |||||
from functools import partial | from functools import partial | ||||
import fnmatch | import fnmatch | ||||
import os | import os | ||||
import re | import re | ||||
import subprocess | import subprocess | ||||
import traceback | import traceback | ||||
from commando.util import getLoggerWithNullHandler, load_python_object | |||||
from fswrap import File | |||||
logger = getLoggerWithNullHandler('hyde.engine') | logger = getLoggerWithNullHandler('hyde.engine') | ||||
class PluginProxy(object): | class PluginProxy(object): | ||||
@@ -263,7 +261,7 @@ class Plugin(object): | |||||
Loads plugins based on the configuration. Assigns the plugins to | Loads plugins based on the configuration. Assigns the plugins to | ||||
'site.plugins' | 'site.plugins' | ||||
""" | """ | ||||
site.plugins = [loader.load_python_object(name)(site) | |||||
site.plugins = [load_python_object(name)(site) | |||||
for name in site.config.plugins] | for name in site.config.plugins] | ||||
@staticmethod | @staticmethod | ||||
@@ -1,8 +1,7 @@ | |||||
import abc | import abc | ||||
from operator import attrgetter | from operator import attrgetter | ||||
from hyde.util import getLoggerWithNullHandler | |||||
from hyde.loader import load_python_object | |||||
from commando.util import getLoggerWithNullHandler, load_python_object | |||||
""" | """ | ||||
Contains abstract classes and utilities that help publishing a website to a | Contains abstract classes and utilities that help publishing a website to a | ||||
@@ -3,8 +3,6 @@ | |||||
Contains classes and utilities for serving a site | Contains classes and utilities for serving a site | ||||
generated from hyde. | generated from hyde. | ||||
""" | """ | ||||
import os | |||||
import select | |||||
import threading | import threading | ||||
import urlparse | import urlparse | ||||
import urllib | import urllib | ||||
@@ -12,12 +10,12 @@ import traceback | |||||
from datetime import datetime | from datetime import datetime | ||||
from SimpleHTTPServer import SimpleHTTPRequestHandler | from SimpleHTTPServer import SimpleHTTPRequestHandler | ||||
from BaseHTTPServer import HTTPServer | from BaseHTTPServer import HTTPServer | ||||
from hyde.fs import File, Folder | |||||
from hyde.site import Site | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.exceptions import HydeException | |||||
from hyde.util import getLoggerWithNullHandler | |||||
from fswrap import File, Folder | |||||
from commando.util import getLoggerWithNullHandler | |||||
logger = getLoggerWithNullHandler('hyde.server') | logger = getLoggerWithNullHandler('hyde.server') | ||||
class HydeRequestHandler(SimpleHTTPRequestHandler): | class HydeRequestHandler(SimpleHTTPRequestHandler): | ||||
@@ -146,44 +144,6 @@ class HydeWebServer(HTTPServer): | |||||
ext = "." + extension if not extension == 'default' else '' | ext = "." + extension if not extension == 'default' else '' | ||||
HydeRequestHandler.extensions_map[ext] = type | HydeRequestHandler.extensions_map[ext] = type | ||||
####### Code from python 2.7.1: Socket server | |||||
####### Duplicated to make sure shutdown works in Python v > 2.6 | |||||
####### | |||||
def serve_forever(self, poll_interval=0.5): | |||||
"""Handle one request at a time until shutdown. | |||||
Polls for shutdown every poll_interval seconds. Ignores | |||||
self.timeout. If you need to do periodic tasks, do them in | |||||
another thread. | |||||
""" | |||||
self.__is_shut_down.clear() | |||||
try: | |||||
while not self.__shutdown_request: | |||||
# XXX: Consider using another file descriptor or | |||||
# connecting to the socket to wake this up instead of | |||||
# polling. Polling reduces our responsiveness to a | |||||
# shutdown request and wastes cpu at all other times. | |||||
r, w, e = select.select([self], [], [], poll_interval) | |||||
if self in r: | |||||
self._handle_request_noblock() | |||||
finally: | |||||
self.__shutdown_request = False | |||||
self.__is_shut_down.set() | |||||
def shutdown(self): | |||||
"""Stops the serve_forever loop. | |||||
Blocks until the loop has finished. This must be called while | |||||
serve_forever() is running in another thread, or it will | |||||
deadlock. | |||||
""" | |||||
self.__shutdown_request = True | |||||
self.__is_shut_down.wait() | |||||
############## Duplication End. | |||||
def regenerate(self): | def regenerate(self): | ||||
""" | """ | ||||
Regenerates the entire site. | Regenerates the entire site. | ||||
@@ -10,10 +10,10 @@ from functools import wraps | |||||
from urllib import quote | from urllib import quote | ||||
from hyde.exceptions import HydeException | from hyde.exceptions import HydeException | ||||
from hyde.fs import FS, File, Folder | |||||
from hyde.model import Config | from hyde.model import Config | ||||
from hyde.util import getLoggerWithNullHandler | |||||
from commando.util import getLoggerWithNullHandler | |||||
from fswrap import FS, File, Folder | |||||
def path_normalized(f): | def path_normalized(f): | ||||
@wraps(f) | @wraps(f) | ||||
@@ -51,7 +51,7 @@ class Processable(object): | |||||
Gets the source path of this node. | Gets the source path of this node. | ||||
""" | """ | ||||
return self.source.path | return self.source.path | ||||
def get_relative_deploy_path(self): | def get_relative_deploy_path(self): | ||||
""" | """ | ||||
Gets the path where the file will be created | Gets the path where the file will be created | ||||
@@ -109,7 +109,7 @@ class Resource(Processable): | |||||
Gets the path relative to the root folder (Content) | Gets the path relative to the root folder (Content) | ||||
""" | """ | ||||
return self.source_file.get_relative_path(self.node.root.source_folder) | return self.source_file.get_relative_path(self.node.root.source_folder) | ||||
@property | @property | ||||
def slug(self): | def slug(self): | ||||
#TODO: Add a more sophisticated slugify method | #TODO: Add a more sophisticated slugify method | ||||
@@ -4,10 +4,12 @@ | |||||
Abstract classes and utilities for template engines | Abstract classes and utilities for template engines | ||||
""" | """ | ||||
from hyde.exceptions import HydeException | from hyde.exceptions import HydeException | ||||
from hyde.util import getLoggerWithNullHandler | |||||
import abc | import abc | ||||
from commando.util import getLoggerWithNullHandler | |||||
class HtmlWrap(object): | class HtmlWrap(object): | ||||
""" | """ | ||||
A wrapper class for raw html. | A wrapper class for raw html. | ||||
@@ -4,13 +4,13 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from pyquery import PyQuery | |||||
from fswrap import File | |||||
from nose.tools import nottest | from nose.tools import nottest | ||||
from pyquery import PyQuery | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,10 +4,10 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File | |||||
from pyquery import PyQuery | from pyquery import PyQuery | ||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,11 +4,11 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.model import Expando | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File, Folder | |||||
COMBINE_SOURCE = File(__file__).parent.child_folder('combine') | COMBINE_SOURCE = File(__file__).parent.child_folder('combine') | ||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,13 +4,10 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from pyquery import PyQuery | |||||
from nose.tools import nottest | |||||
from fswrap import File | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,11 +4,12 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from hyde.model import Expando, Config | |||||
from hyde.model import Config | |||||
from fswrap import File | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -60,7 +61,7 @@ class TestFlattner(object): | |||||
gen = Generator(s) | gen = Generator(s) | ||||
gen.generate_all() | gen.generate_all() | ||||
blog_node = s.content.node_from_relative_path('blog') | blog_node = s.content.node_from_relative_path('blog') | ||||
assert blog_node | assert blog_node | ||||
assert blog_node.url == '/' | assert blog_node.url == '/' | ||||
@@ -7,11 +7,11 @@ Use nose | |||||
from hyde.ext.plugins.meta import MetaPlugin | from hyde.ext.plugins.meta import MetaPlugin | ||||
from hyde.ext.plugins.sorter import SorterPlugin | from hyde.ext.plugins.sorter import SorterPlugin | ||||
from hyde.ext.plugins.grouper import GrouperPlugin | from hyde.ext.plugins.grouper import GrouperPlugin | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from hyde.model import Config, Expando | from hyde.model import Config, Expando | ||||
from fswrap import File | |||||
from hyde.tests.util import assert_html_equals | from hyde.tests.util import assert_html_equals | ||||
import yaml | import yaml | ||||
@@ -6,11 +6,11 @@ Use nose | |||||
Requires PIL | Requires PIL | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from pyquery import PyQuery | |||||
from fswrap import File | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
IMAGE_SOURCE = File(__file__).parent.child_folder('optipng') | IMAGE_SOURCE = File(__file__).parent.child_folder('optipng') | ||||
@@ -87,8 +87,7 @@ class TestImageSizer(object): | |||||
def test_size_image_multiline(self): | def test_size_image_multiline(self): | ||||
text = u""" | text = u""" | ||||
<img | |||||
src="/media/img/%s"> | |||||
<img src="/media/img/%s"> | |||||
""" % IMAGE_NAME | """ % IMAGE_NAME | ||||
html = self._generic_test_image(text) | html = self._generic_test_image(text) | ||||
assert ' width="%d"' % IMAGE_SIZE[0] in html | assert ' width="%d"' % IMAGE_SIZE[0] in html | ||||
@@ -4,10 +4,11 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File, Folder | |||||
LESS_SOURCE = File(__file__).parent.child_folder('less') | LESS_SOURCE = File(__file__).parent.child_folder('less') | ||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,10 +4,10 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File | |||||
from pyquery import PyQuery | from pyquery import PyQuery | ||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,10 +4,10 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File, Folder | |||||
from pyquery import PyQuery | from pyquery import PyQuery | ||||
import yaml | import yaml | ||||
@@ -4,11 +4,12 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.model import Expando | from hyde.model import Expando | ||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File, Folder | |||||
OPTIPNG_SOURCE = File(__file__).parent.child_folder('optipng') | OPTIPNG_SOURCE = File(__file__).parent.child_folder('optipng') | ||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -6,11 +6,11 @@ Use nose | |||||
""" | """ | ||||
from textwrap import dedent | from textwrap import dedent | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.model import Expando | |||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
class TestPaginator(object): | class TestPaginator(object): | ||||
@@ -6,13 +6,13 @@ Use nose | |||||
""" | """ | ||||
from hyde.ext.plugins.meta import MetaPlugin | from hyde.ext.plugins.meta import MetaPlugin | ||||
from hyde.ext.plugins.sorter import SorterPlugin | from hyde.ext.plugins.sorter import SorterPlugin | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from hyde.model import Config, Expando | from hyde.model import Config, Expando | ||||
from fswrap import File, Folder | |||||
import yaml | import yaml | ||||
from operator import attrgetter | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,15 +4,15 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.model import Expando | from hyde.model import Expando | ||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File, Folder | |||||
STYLUS_SOURCE = File(__file__).parent.child_folder('stylus') | STYLUS_SOURCE = File(__file__).parent.child_folder('stylus') | ||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
class TestStylus(object): | class TestStylus(object): | ||||
def setUp(self): | def setUp(self): | ||||
@@ -4,10 +4,10 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File | |||||
from pyquery import PyQuery | from pyquery import PyQuery | ||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,11 +4,10 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -152,7 +151,7 @@ class TestTagger(object): | |||||
assert tag | assert tag | ||||
assert not hasattr(tag, "emotions") | assert not hasattr(tag, "emotions") | ||||
def test_tagger_metadata(self): | |||||
def test_tagger_sorted(self): | |||||
conf = { | conf = { | ||||
"tagger":{ | "tagger":{ | ||||
"sorter": "time", | "sorter": "time", | ||||
@@ -4,12 +4,11 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from urllib import quote | from urllib import quote | ||||
from pyquery import PyQuery | |||||
from fswrap import File | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,11 +4,12 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.model import Expando | from hyde.model import Expando | ||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File, Folder | |||||
UGLIFY_SOURCE = File(__file__).parent.child_folder('uglify') | UGLIFY_SOURCE = File(__file__).parent.child_folder('uglify') | ||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -4,14 +4,13 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.model import Config | |||||
from hyde.site import Site | from hyde.site import Site | ||||
from fswrap import File, Folder | |||||
import yaml | import yaml | ||||
from hyde.model import Config | |||||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
@@ -1,457 +0,0 @@ | |||||
# -*- coding: utf-8 -*- | |||||
""" | |||||
Use nose | |||||
`$ pip install nose` | |||||
`$ nosetests` | |||||
""" | |||||
from hyde.fs import FS, File, Folder | |||||
import codecs | |||||
import os | |||||
import shutil | |||||
from nose.tools import raises, with_setup, nottest | |||||
def test_representation(): | |||||
f = FS(__file__) | |||||
assert f.path == __file__ | |||||
assert unicode(f) == __file__ | |||||
assert repr(f) == __file__ | |||||
def test_name(): | |||||
f = FS(__file__) | |||||
assert f.name == os.path.basename(__file__) | |||||
def test_equals(): | |||||
f = FS('/blog/2010/december') | |||||
g = FS('/blog/2010/december') | |||||
h = FS('/blog/2010/december/') | |||||
i = FS('/blog/2010/november') | |||||
assert f == f.path | |||||
assert f == g | |||||
assert f == h | |||||
assert f != i | |||||
def test_name_without_extension(): | |||||
f = File(__file__) | |||||
assert f.name_without_extension == "test_fs" | |||||
def test_extension(): | |||||
f = File(__file__) | |||||
assert f.extension == os.path.splitext(__file__)[1] | |||||
f = File("abc") | |||||
assert f.extension == '' | |||||
def test_kind(): | |||||
f = File(__file__) | |||||
assert f.kind == os.path.splitext(__file__)[1].lstrip('.') | |||||
f = File("abc") | |||||
assert f.kind == '' | |||||
def test_can_create_temp_file(): | |||||
text = "A for apple" | |||||
f = File.make_temp(text) | |||||
assert f.exists | |||||
assert text == f.read_all() | |||||
f.delete() | |||||
assert not f.exists | |||||
def test_time_functions(): | |||||
f1 = File(__file__) | |||||
t1 = f1.last_modified | |||||
f2 = File.make_temp("I am new") | |||||
t2 = f2.last_modified | |||||
assert t1 < t2 | |||||
assert f2.has_changed_since(t1) | |||||
assert f1.older_than(f2) | |||||
def test_path_expands_user(): | |||||
f = File("~/abc/def") | |||||
assert f.path == os.path.expanduser("~/abc/def") | |||||
def test_fully_expanded_path(): | |||||
f = File(__file__).parent | |||||
n = f.child_folder('../' + f.name) | |||||
e = Folder(n.fully_expanded_path) | |||||
assert n != e | |||||
assert f == e | |||||
def test_parent(): | |||||
f = File(__file__) | |||||
p = f.parent | |||||
assert hasattr(p, 'child_folder') | |||||
assert unicode(p) == os.path.dirname(__file__) | |||||
def test_child(): | |||||
p = File(__file__).parent | |||||
c = p.child('data.dat') | |||||
assert c == os.path.join(os.path.dirname(__file__), 'data.dat') | |||||
def test_child_folder(): | |||||
p = File(__file__).parent | |||||
c = p.child_folder('data') | |||||
assert hasattr(c, 'child_folder') | |||||
assert unicode(c) == os.path.join(os.path.dirname(__file__), 'data') | |||||
def test_exists(): | |||||
p = FS(__file__) | |||||
assert p.exists | |||||
p = FS(__file__ + "_some_junk") | |||||
assert not p.exists | |||||
f = FS(__file__).parent.parent | |||||
assert f.exists | |||||
f = FS(__file__).parent.child_folder('templates') | |||||
assert f.exists | |||||
def test_create_folder(): | |||||
f = FS(__file__).parent | |||||
assert f.exists | |||||
f.make() | |||||
assert True # No Exceptions | |||||
c = f.child_folder('__test__') | |||||
assert not c.exists | |||||
c.make() | |||||
assert c.exists | |||||
shutil.rmtree(unicode(c)) | |||||
assert not c.exists | |||||
def test_remove_folder(): | |||||
f = FS(__file__).parent | |||||
c = f.child_folder('__test__') | |||||
assert not c.exists | |||||
c.make() | |||||
assert c.exists | |||||
c.delete() | |||||
assert not c.exists | |||||
def test_can_remove_file(): | |||||
f = FS(__file__).parent | |||||
c = f.child_folder('__test__') | |||||
c.make() | |||||
assert c.exists | |||||
txt = "abc" | |||||
abc = File(c.child('abc.txt')) | |||||
abc.write(txt) | |||||
assert abc.exists | |||||
abc.delete() | |||||
assert not abc.exists | |||||
abc.delete() | |||||
assert True # No Exception | |||||
c.delete() | |||||
def test_file_or_folder(): | |||||
f = FS.file_or_folder(__file__) | |||||
assert isinstance(f, File) | |||||
f = FS.file_or_folder(File(__file__).parent) | |||||
assert isinstance(f, Folder) | |||||
DATA_ROOT = File(__file__).parent.child_folder('data') | |||||
TEMPLATE_ROOT = File(__file__).parent.child_folder('templates') | |||||
JINJA2 = TEMPLATE_ROOT.child_folder('jinja2') | |||||
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 | |||||
next = JINJA2 | |||||
for folder in INDEX.ancestors(): | |||||
assert folder == next | |||||
depth += 1 | |||||
next = folder.parent | |||||
assert depth == len(JINJA2.path.split(os.sep)) | |||||
def test_ancestors_stop(): | |||||
depth = 0 | |||||
next = JINJA2 | |||||
for folder in INDEX.ancestors(stop=TEMPLATE_ROOT.parent): | |||||
assert folder == next | |||||
depth += 1 | |||||
next = folder.parent | |||||
assert depth == 2 | |||||
def test_is_descendant_of(): | |||||
assert INDEX.is_descendant_of(JINJA2) | |||||
assert JINJA2.is_descendant_of(TEMPLATE_ROOT) | |||||
assert INDEX.is_descendant_of(TEMPLATE_ROOT) | |||||
assert not INDEX.is_descendant_of(DATA_ROOT) | |||||
def test_get_relative_path(): | |||||
assert INDEX.get_relative_path(TEMPLATE_ROOT) == Folder(JINJA2.name).child(INDEX.name) | |||||
assert INDEX.get_relative_path(TEMPLATE_ROOT.parent) == Folder( | |||||
TEMPLATE_ROOT.name).child_folder(JINJA2.name).child(INDEX.name) | |||||
assert JINJA2.get_relative_path(JINJA2) == "" | |||||
def test_get_mirror(): | |||||
mirror = JINJA2.get_mirror(DATA_ROOT, source_root=TEMPLATE_ROOT) | |||||
assert mirror == DATA_ROOT.child_folder(JINJA2.name) | |||||
mirror = JINJA2.get_mirror(DATA_ROOT, source_root=TEMPLATE_ROOT.parent) | |||||
assert mirror == DATA_ROOT.child_folder(TEMPLATE_ROOT.name).child_folder(JINJA2.name) | |||||
def test_mimetype(): | |||||
assert HELPERS.mimetype == 'text/html' | |||||
assert LOGO.mimetype == 'image/png' | |||||
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 | |||||
assert LOGO.is_image | |||||
def test_file_size(): | |||||
assert LOGO.size == 1942 | |||||
@nottest | |||||
def setup_data(): | |||||
DATA_ROOT.make() | |||||
@nottest | |||||
def cleanup_data(): | |||||
DATA_ROOT.delete() | |||||
@with_setup(setup_data, cleanup_data) | |||||
def test_copy_file(): | |||||
DATA_HELPERS = File(DATA_ROOT.child(HELPERS.name)) | |||||
assert not DATA_HELPERS.exists | |||||
HELPERS.copy_to(DATA_ROOT) | |||||
assert DATA_HELPERS.exists | |||||
@with_setup(setup_data, cleanup_data) | |||||
def test_copy_folder(): | |||||
assert DATA_ROOT.exists | |||||
DATA_JINJA2 = DATA_ROOT.child_folder(JINJA2.name) | |||||
assert not DATA_JINJA2.exists | |||||
JINJA2.copy_to(DATA_ROOT) | |||||
assert DATA_JINJA2.exists | |||||
for f in [HELPERS, INDEX, LAYOUT]: | |||||
assert File(DATA_JINJA2.child(f.name)).exists | |||||
@with_setup(setup_data, cleanup_data) | |||||
def test_copy_folder_target_missing(): | |||||
DATA_ROOT.delete() | |||||
assert not DATA_ROOT.exists | |||||
DATA_JINJA2 = DATA_ROOT.child_folder(JINJA2.name) | |||||
assert not DATA_JINJA2.exists | |||||
JINJA2.copy_to(DATA_ROOT) | |||||
assert DATA_JINJA2.exists | |||||
for f in [HELPERS, INDEX, LAYOUT]: | |||||
assert File(DATA_JINJA2.child(f.name)).exists | |||||
@with_setup(setup_data, cleanup_data) | |||||
def test_copy_folder_contents(): | |||||
for f in [HELPERS, INDEX, LAYOUT]: | |||||
assert not File(DATA_ROOT.child(f.name)).exists | |||||
JINJA2.copy_contents_to(DATA_ROOT) | |||||
for f in [HELPERS, INDEX, LAYOUT]: | |||||
assert File(DATA_ROOT.child(f.name)).exists | |||||
@with_setup(setup_data, cleanup_data) | |||||
def test_move_folder(): | |||||
DATA_JUNK = DATA_ROOT.child_folder('junk') | |||||
assert not DATA_JUNK.exists | |||||
JINJA2.copy_contents_to(DATA_JUNK) | |||||
assert DATA_JUNK.exists | |||||
for f in [HELPERS, INDEX, LAYOUT]: | |||||
assert File(DATA_JUNK.child(f.name)).exists | |||||
DATA_JUNK2 = DATA_ROOT.child_folder('second_junk') | |||||
assert not DATA_JUNK2.exists | |||||
DATA_JUNK.move_to(DATA_JUNK2) | |||||
assert not DATA_JUNK.exists | |||||
assert DATA_JUNK2.exists | |||||
for f in [HELPERS, INDEX, LAYOUT]: | |||||
assert File(DATA_JUNK2.child_folder( | |||||
DATA_JUNK.name).child( | |||||
f.name)).exists | |||||
@with_setup(setup_data, cleanup_data) | |||||
def test_rename_folder(): | |||||
DATA_JUNK = DATA_ROOT.child_folder('junk') | |||||
assert not DATA_JUNK.exists | |||||
JINJA2.copy_contents_to(DATA_JUNK) | |||||
for f in [HELPERS, INDEX, LAYOUT]: | |||||
assert File(DATA_JUNK.child(f.name)).exists | |||||
DATA_JUNK2 = DATA_ROOT.child_folder('junk2') | |||||
assert DATA_JUNK.exists | |||||
assert not DATA_JUNK2.exists | |||||
DATA_JUNK.rename_to('junk2') | |||||
assert not DATA_JUNK.exists | |||||
assert DATA_JUNK2.exists | |||||
for f in [HELPERS, INDEX, LAYOUT]: | |||||
assert File(DATA_JUNK2.child(f.name)).exists | |||||
@with_setup(setup_data, cleanup_data) | |||||
def test_read_all(): | |||||
utxt = u'ĂĄĂźcdeĆ’' | |||||
path = DATA_ROOT.child('unicode.txt') | |||||
with codecs.open(path, 'w', 'utf-8') as f: | |||||
f.write(utxt) | |||||
txt = File(path).read_all() | |||||
assert txt == utxt | |||||
@with_setup(setup_data, cleanup_data) | |||||
def test_write(): | |||||
utxt = u'ĂĄĂźcdeĆ’' | |||||
path = DATA_ROOT.child('unicode.txt') | |||||
File(path).write(utxt) | |||||
txt = File(path).read_all() | |||||
assert txt == utxt | |||||
def test_walker(): | |||||
folders = [] | |||||
files = [] | |||||
complete = [] | |||||
with TEMPLATE_ROOT.walker as walker: | |||||
@walker.folder_visitor | |||||
def visit_folder(f): | |||||
folders.append(f) | |||||
@walker.file_visitor | |||||
def visit_file(f): | |||||
files.append(f) | |||||
@walker.finalizer | |||||
def visit_complete(): | |||||
assert folders[0] == TEMPLATE_ROOT | |||||
assert folders[1] == JINJA2 | |||||
assert INDEX in files | |||||
assert HELPERS in files | |||||
assert LAYOUT in files | |||||
complete.append(True) | |||||
assert len(files) == 4 | |||||
assert len(folders) == 2 | |||||
assert len(complete) == 1 | |||||
def test_walker_walk_all(): | |||||
items = list(TEMPLATE_ROOT.walker.walk_all()) | |||||
assert len(items) == 6 | |||||
assert TEMPLATE_ROOT in items | |||||
assert JINJA2 in items | |||||
assert INDEX in items | |||||
assert HELPERS in items | |||||
assert LAYOUT in items | |||||
def test_walker_walk_files(): | |||||
items = list(TEMPLATE_ROOT.walker.walk_files()) | |||||
assert len(items) == 4 | |||||
assert INDEX in items | |||||
assert HELPERS in items | |||||
assert LAYOUT in items | |||||
def test_walker_walk_folders(): | |||||
items = list(TEMPLATE_ROOT.walker.walk_folders()) | |||||
assert len(items) == 2 | |||||
assert TEMPLATE_ROOT in items | |||||
assert JINJA2 in items | |||||
def test_walker_templates_just_root(): | |||||
folders = [] | |||||
files = [] | |||||
complete = [] | |||||
with TEMPLATE_ROOT.walker as walker: | |||||
@walker.folder_visitor | |||||
def visit_folder(f): | |||||
assert f == TEMPLATE_ROOT | |||||
folders.append(f) | |||||
return False | |||||
@walker.file_visitor | |||||
def visit_file(f): | |||||
files.append(f) | |||||
@walker.finalizer | |||||
def visit_complete(): | |||||
complete.append(True) | |||||
assert len(files) == 0 | |||||
assert len(folders) == 1 | |||||
assert len(complete) == 1 | |||||
def test_lister_templates(): | |||||
folders = [] | |||||
files = [] | |||||
complete = [] | |||||
with TEMPLATE_ROOT.lister as lister: | |||||
@lister.folder_visitor | |||||
def visit_folder(f): | |||||
assert f == JINJA2 | |||||
folders.append(f) | |||||
@lister.file_visitor | |||||
def visit_file(f): | |||||
files.append(f) | |||||
@lister.finalizer | |||||
def visit_complete(): | |||||
complete.append(True) | |||||
assert len(files) == 0 | |||||
assert len(folders) == 1 | |||||
assert len(complete) == 1 | |||||
def test_lister_list_all(): | |||||
items = list(TEMPLATE_ROOT.lister.list_all()) | |||||
assert len(items) == 1 | |||||
assert JINJA2 in items | |||||
items = list(JINJA2.lister.list_all()) | |||||
assert len(items) == 4 | |||||
assert INDEX in items | |||||
assert HELPERS in items | |||||
assert LAYOUT in items | |||||
def test_lister_list_files(): | |||||
items = list(TEMPLATE_ROOT.lister.list_files()) | |||||
assert len(items) == 0 | |||||
items = list(JINJA2.lister.list_files()) | |||||
assert len(items) == 4 | |||||
assert INDEX in items | |||||
assert HELPERS in items | |||||
assert LAYOUT in items | |||||
def test_lister_list_folders(): | |||||
items = list(TEMPLATE_ROOT.lister.list_folders()) | |||||
assert len(items) == 1 | |||||
assert JINJA2 in items | |||||
items = list(JINJA2.lister.list_folders()) | |||||
assert len(items) == 0 | |||||
def test_lister_jinja2(): | |||||
folders = [] | |||||
files = [] | |||||
complete = [] | |||||
with JINJA2.lister as lister: | |||||
@lister.folder_visitor | |||||
def visit_folder(f): | |||||
folders.append(f) | |||||
@lister.file_visitor | |||||
def visit_file(f): | |||||
files.append(f) | |||||
@lister.finalizer | |||||
def visit_complete(): | |||||
assert INDEX in files | |||||
assert HELPERS in files | |||||
assert LAYOUT in files | |||||
complete.append(True) | |||||
assert len(files) == 4 | |||||
assert len(folders) == 0 | |||||
assert len(complete) == 1 |
@@ -6,13 +6,13 @@ Use nose | |||||
""" | """ | ||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.fs import FS, File, Folder | |||||
from hyde.model import Config | from hyde.model import Config | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from nose.tools import raises, with_setup, nottest | |||||
from pyquery import PyQuery | from pyquery import PyQuery | ||||
from fswrap import File, Folder | |||||
TEST_SITE = File(__file__).parent.child_folder('_test') | TEST_SITE = File(__file__).parent.child_folder('_test') | ||||
class TestGenerator(object): | class TestGenerator(object): | ||||
@@ -8,8 +8,9 @@ Use nose | |||||
from hyde.engine import Engine | from hyde.engine import Engine | ||||
from hyde.exceptions import HydeException | from hyde.exceptions import HydeException | ||||
from hyde.fs import File, Folder | |||||
from hyde.layout import Layout | from hyde.layout import Layout | ||||
from fswrap import File, Folder | |||||
from nose.tools import raises, with_setup, nottest | from nose.tools import raises, with_setup, nottest | ||||
TEST_SITE = File(__file__).parent.child_folder('_test') | TEST_SITE = File(__file__).parent.child_folder('_test') | ||||
@@ -7,18 +7,19 @@ Use nose | |||||
Some code borrowed from rwbench.py from the jinja2 examples | Some code borrowed from rwbench.py from the jinja2 examples | ||||
""" | """ | ||||
from datetime import datetime | from datetime import datetime | ||||
from random import choice, randrange | |||||
from hyde.ext.templates.jinja import Jinja2Template | from hyde.ext.templates.jinja import Jinja2Template | ||||
from hyde.fs import File | |||||
from hyde.site import Site | from hyde.site import Site | ||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.model import Config | from hyde.model import Config | ||||
from fswrap import File | |||||
from jinja2.utils import generate_lorem_ipsum | from jinja2.utils import generate_lorem_ipsum | ||||
from random import choice, randrange | |||||
import yaml | |||||
from pyquery import PyQuery | |||||
from nose.tools import nottest | from nose.tools import nottest | ||||
from pyquery import PyQuery | |||||
import yaml | |||||
ROOT = File(__file__).parent | ROOT = File(__file__).parent | ||||
JINJA2 = ROOT.child_folder('templates/jinja2') | JINJA2 = ROOT.child_folder('templates/jinja2') | ||||
@@ -623,7 +624,7 @@ Hyde & Jinja. | |||||
assert "reference" not in html | assert "reference" not in html | ||||
def test_yaml_tag(salf): | |||||
def test_yaml_tag(self): | |||||
text = """ | text = """ | ||||
{% yaml test %} | {% yaml test %} | ||||
@@ -4,11 +4,12 @@ Use nose | |||||
`$ pip install nose` | `$ pip install nose` | ||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
import os | |||||
from hyde.fs import File, Folder | |||||
from hyde.layout import Layout, HYDE_DATA, LAYOUTS | from hyde.layout import Layout, HYDE_DATA, LAYOUTS | ||||
from nose.tools import raises, with_setup, nottest | |||||
import os | |||||
from fswrap import File | |||||
from nose.tools import nottest, with_setup | |||||
DATA_ROOT = File(__file__).parent.child_folder('data') | DATA_ROOT = File(__file__).parent.child_folder('data') | ||||
LAYOUT_ROOT = DATA_ROOT.child_folder(LAYOUTS) | LAYOUT_ROOT = DATA_ROOT.child_folder(LAYOUTS) | ||||
@@ -1,81 +0,0 @@ | |||||
# -*- coding: utf-8 -*- | |||||
""" | |||||
Use nose | |||||
`$ pip install nose` | |||||
`$ nosetests` | |||||
""" | |||||
from hyde.loader import load_python_object | |||||
from nose.tools import raises | |||||
import os | |||||
from hyde.exceptions import HydeException | |||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | |||||
from hyde.site import Site | |||||
def test_can_load_locals(): | |||||
file_class = load_python_object('hyde.fs.File') | |||||
assert file_class | |||||
f = file_class(__file__) | |||||
assert f | |||||
assert f.name == os.path.basename(__file__) | |||||
def test_can_load_from_python_path(): | |||||
markdown = load_python_object('markdown.markdown') | |||||
assert markdown | |||||
assert "<h3>h3</h3>" == markdown("### h3") | |||||
def test_can_load_module_without_dot(): | |||||
yaml = load_python_object('yaml') | |||||
abc = yaml.load(""" | |||||
d: efg | |||||
l: mno | |||||
""") | |||||
assert abc['d'] == 'efg' | |||||
assert abc['l'] == 'mno' | |||||
def test_can_load_site_specific_plugins(): | |||||
TEST_SITE = File(__file__).parent.child_folder('_test') | |||||
TEST_SITE.make() | |||||
TEST_SITE.parent.child_folder( | |||||
'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
TEST_SITE.parent.child_folder( | |||||
'ssp').copy_contents_to(TEST_SITE) | |||||
s = Site(TEST_SITE) | |||||
gen = Generator(s) | |||||
gen.generate_all() | |||||
banner = """ | |||||
<!-- | |||||
This file was produced with infinite love, care & sweat. | |||||
Please dont copy. If you have to, please drop me a note. | |||||
--> | |||||
""" | |||||
with TEST_SITE.child_folder('deploy').get_walker('*.html') as walker: | |||||
@walker.file_visitor | |||||
def visit_file(f): | |||||
text = f.read_all() | |||||
assert text.strip().startswith(banner.strip()) | |||||
@raises(HydeException) | |||||
def test_exception_raised_for_invalid_module(): | |||||
load_python_object("junk.junk.junk") | |||||
assert False | |||||
@raises(HydeException) | |||||
def test_exception_raised_for_invalid_object(): | |||||
load_python_object("markdown.junk") | |||||
assert False |
@@ -5,7 +5,8 @@ Use nose | |||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.model import Config, Expando | from hyde.model import Config, Expando | ||||
from hyde.fs import * | |||||
from fswrap import File, Folder | |||||
def test_expando_one_level(): | def test_expando_one_level(): | ||||
d = {"a": 123, "b": "abc"} | d = {"a": 123, "b": "abc"} | ||||
@@ -5,16 +5,14 @@ Use nose | |||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
from hyde.exceptions import HydeException | |||||
from hyde.fs import File, Folder | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from hyde.model import Expando | from hyde.model import Expando | ||||
from mock import patch, Mock | from mock import patch, Mock | ||||
from nose.tools import raises, nottest, with_setup | |||||
from fswrap import File, Folder | |||||
TEST_SITE = File(__file__).parent.child_folder('_test') | TEST_SITE = File(__file__).parent.child_folder('_test') | ||||
@@ -18,14 +18,14 @@ Use nose | |||||
`$ nosetests` | `$ nosetests` | ||||
""" | """ | ||||
import yaml | import yaml | ||||
from urllib import quote | |||||
from hyde.fs import File, Folder | |||||
from hyde.model import Config, Expando | |||||
from hyde.site import Node, RootNode, Site | |||||
from hyde.model import Config | |||||
from hyde.site import Site | |||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from nose.tools import raises, with_setup, nottest | |||||
from fswrap import File | |||||
from nose.tools import nottest | |||||
TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | ||||
@@ -7,11 +7,10 @@ Use nose | |||||
import yaml | import yaml | ||||
from urllib import quote | from urllib import quote | ||||
from hyde.fs import File, Folder | |||||
from hyde.model import Config, Expando | |||||
from hyde.model import Config | |||||
from hyde.site import Node, RootNode, Site | from hyde.site import Node, RootNode, Site | ||||
from nose.tools import raises, with_setup, nottest | |||||
from fswrap import File, Folder | |||||
TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | ||||
@@ -1,97 +1,10 @@ | |||||
""" | """ | ||||
Module for python 2.6 compatibility. | Module for python 2.6 compatibility. | ||||
""" | """ | ||||
import logging | |||||
import os | import os | ||||
import sys | |||||
from itertools import ifilter, izip, tee | |||||
from functools import partial | |||||
from itertools import izip, tee | |||||
try: | |||||
from logging import NullHandler | |||||
except: | |||||
class NullHandler(logging.Handler): | |||||
""" | |||||
NOOP handler for libraries. | |||||
""" | |||||
def emit(self, record): | |||||
""" | |||||
/dev/null | |||||
""" | |||||
pass | |||||
def getLoggerWithConsoleHandler(logger_name): | |||||
logger = logging.getLogger(logger_name) | |||||
logger.setLevel(logging.INFO) | |||||
if not logger.handlers: | |||||
handler = logging.StreamHandler(sys.stdout) | |||||
if sys.platform == 'win32': | |||||
formatter = logging.Formatter( | |||||
fmt="%(asctime)s %(name)s %(message)s", | |||||
datefmt='%H:%M:%S') | |||||
else: | |||||
formatter = ColorFormatter(fmt="$RESET %(asctime)s " | |||||
"$BOLD$COLOR%(name)s$RESET " | |||||
"%(message)s", datefmt='%H:%M:%S') | |||||
handler.setFormatter(formatter) | |||||
logger.addHandler(handler) | |||||
return logger | |||||
def getLoggerWithNullHandler(logger_name): | |||||
""" | |||||
Gets the logger initialized with the `logger_name` | |||||
and a NullHandler. | |||||
""" | |||||
logger = logging.getLogger(logger_name) | |||||
if not logger.handlers: | |||||
logger.addHandler(NullHandler()) | |||||
return logger | |||||
## Code stolen from : | |||||
## http://stackoverflow.com/questions/384076/how-can-i-make-the-python-logging-output-to-be-colored/2532931#2532931 | |||||
## | |||||
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) | |||||
COLORS = { | |||||
'WARNING' : YELLOW, | |||||
'INFO' : WHITE, | |||||
'DEBUG' : BLUE, | |||||
'CRITICAL' : YELLOW, | |||||
'ERROR' : RED, | |||||
'RED' : RED, | |||||
'GREEN' : GREEN, | |||||
'YELLOW' : YELLOW, | |||||
'BLUE' : BLUE, | |||||
'MAGENTA' : MAGENTA, | |||||
'CYAN' : CYAN, | |||||
'WHITE' : WHITE, | |||||
} | |||||
RESET_SEQ = "\033[0m" | |||||
COLOR_SEQ = "\033[1;%dm" | |||||
BOLD_SEQ = "\033[1m" | |||||
class ColorFormatter(logging.Formatter): | |||||
def __init__(self, *args, **kwargs): | |||||
# can't do super(...) here because Formatter is an old school class | |||||
logging.Formatter.__init__(self, *args, **kwargs) | |||||
def format(self, record): | |||||
levelname = record.levelname | |||||
color = COLOR_SEQ % (30 + COLORS[levelname]) | |||||
message = logging.Formatter.format(self, record) | |||||
message = message.replace("$RESET", RESET_SEQ)\ | |||||
.replace("$BOLD", BOLD_SEQ)\ | |||||
.replace("$COLOR", color) | |||||
for k,v in COLORS.items(): | |||||
message = message.replace("$" + k, COLOR_SEQ % (v+30))\ | |||||
.replace("$BG" + k, COLOR_SEQ % (v+40))\ | |||||
.replace("$BG-" + k, COLOR_SEQ % (v+40)) | |||||
return message + RESET_SEQ | |||||
logging.ColorFormatter = ColorFormatter | |||||
def make_method(method_name, method_): | def make_method(method_name, method_): | ||||
def method__(*args, **kwargs): | def method__(*args, **kwargs): | ||||
@@ -99,21 +12,23 @@ def make_method(method_name, method_): | |||||
method__.__name__ = method_name | method__.__name__ = method_name | ||||
return method__ | return method__ | ||||
def add_property(obj, method_name, method_, *args, **kwargs): | def add_property(obj, method_name, method_, *args, **kwargs): | ||||
from functools import partial | |||||
m = make_method(method_name, partial(method_, *args, **kwargs)) | m = make_method(method_name, partial(method_, *args, **kwargs)) | ||||
setattr(obj, method_name, property(m)) | setattr(obj, method_name, property(m)) | ||||
def add_method(obj, method_name, method_, *args, **kwargs): | def add_method(obj, method_name, method_, *args, **kwargs): | ||||
from functools import partial | |||||
m = make_method(method_name, partial(method_, *args, **kwargs)) | m = make_method(method_name, partial(method_, *args, **kwargs)) | ||||
setattr(obj, method_name, m) | setattr(obj, method_name, m) | ||||
def pairwalk(iterable): | def pairwalk(iterable): | ||||
a, b = tee(iterable) | a, b = tee(iterable) | ||||
next(b, None) | next(b, None) | ||||
return izip(a, b) | return izip(a, b) | ||||
def first_match(predicate, iterable): | def first_match(predicate, iterable): | ||||
""" | """ | ||||
Gets the first element matched by the predicate | Gets the first element matched by the predicate | ||||
@@ -124,6 +39,7 @@ def first_match(predicate, iterable): | |||||
return item | return item | ||||
return None | return None | ||||
def discover_executable(name, sitepath): | def discover_executable(name, sitepath): | ||||
""" | """ | ||||
Finds an executable in the given sitepath or in the | Finds an executable in the given sitepath or in the | ||||
@@ -1,3 +1,4 @@ | |||||
fswrap==0.1.1 | |||||
commando==0.3.2a | commando==0.3.2a | ||||
PyYAML==3.10 | PyYAML==3.10 | ||||
Markdown==2.3.1 | Markdown==2.3.1 | ||||
@@ -116,6 +116,7 @@ setup(name=PROJECT, | |||||
packages=find_packages(), | packages=find_packages(), | ||||
requires=['python (>= 2.7)'], | requires=['python (>= 2.7)'], | ||||
install_requires=( | install_requires=( | ||||
'fswrap==0.1.1', | |||||
'commando==0.3.2a', | 'commando==0.3.2a', | ||||
'PyYAML==3.10', | 'PyYAML==3.10', | ||||
'Markdown==2.3.1', | 'Markdown==2.3.1', | ||||