* 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', | ||||