| @@ -14,3 +14,4 @@ build | |||
| .idea | |||
| PYSMELLTAGS | |||
| .noseids | |||
| *.tar.gz | |||
| @@ -0,0 +1,6 @@ | |||
| exclude *.pyc .DS_Store .gitignore .noseids MANIFEST.in | |||
| include setup.py | |||
| include distribute_setup.py | |||
| recursive-include hyde *.py | |||
| recursive-include hyde/tests *.py | |||
| recursive-include hyde/layouts *.* | |||
| @@ -1,13 +1,14 @@ | |||
| Version 0.7b1 | |||
| # A brand new **hyde** | |||
| This is the new version of hyde under active development. | |||
| Incomplete documentation can be found [here][hydedocs]. | |||
| [This][hyde1-0] should give a good understanding of the motivation behind this | |||
| version. You can also take a look at the [cloudpanic source][cp] for a | |||
| reference implementation. | |||
| This is the new version of hyde under active development. Incomplete documentation | |||
| can be found [here][hydedocs]. [This][hyde1-0] should give a good understanding of | |||
| the motivation behind this version. You can also take a look at the | |||
| [documentation source][docs] for a reference implementation. | |||
| [hyde1-0]: http://groups.google.com/group/hyde-dev/web/hyde-1-0 | |||
| [cp]: http://github.com/tipiirai/cloudpanic/tree/refactor | |||
| [docs]: https://github.com/hyde/hyde/tree/master/hyde/layouts/doc | |||
| [hydedocs]: http://hyde.github.com/overview | |||
| [Here](http://groups.google.com/group/hyde-dev/browse_thread/thread/2a143bd2081b3322) is | |||
| @@ -15,31 +16,22 @@ the initial announcement of the project. | |||
| ## Installation | |||
| Hyde supports both python 2.7 and 2.6. | |||
| pip install -r req-2.6.txt | |||
| or | |||
| pip install -r req-2.7.txt | |||
| To get the latest released version: | |||
| will install all the dependencies of hyde. | |||
| pip install hyde | |||
| You can choose to install hyde by running | |||
| For the current trunk: | |||
| python setup.py install | |||
| pip install -e git://github.com/hyde/hyde.git#egg=hyde | |||
| ## Creating a new hyde site | |||
| The new version of Hyde uses the `argparse` module and hence support subcommands. | |||
| The following command: | |||
| hyde -s ~/test_site create -l test | |||
| hyde -s ~/test_site create -l doc | |||
| will create a new hyde site using the test layout. | |||
| ## Generating the hyde site | |||
| cd ~/test_site | |||
| @@ -56,6 +48,11 @@ The server also regenerates on demand. As long as the server is running, | |||
| you can make changes to your source and refresh the browser to view the changes. | |||
| ## Examples | |||
| 1. [Cloudpanic](https://github.com/tipiirai/cloudpanic) | |||
| 2. [Ringce](https://github.com/lakshmivyas/ringce/tree/v3.0) | |||
| ## A brief list of features | |||
| @@ -0,0 +1,485 @@ | |||
| #!python | |||
| """Bootstrap distribute installation | |||
| If you want to use setuptools in your package's setup.py, just include this | |||
| file in the same directory with it, and add this to the top of your setup.py:: | |||
| from distribute_setup import use_setuptools | |||
| use_setuptools() | |||
| If you want to require a specific version of setuptools, set a download | |||
| mirror, or use an alternate download directory, you can do so by supplying | |||
| the appropriate options to ``use_setuptools()``. | |||
| This file can also be run as a script to install or upgrade setuptools. | |||
| """ | |||
| import os | |||
| import sys | |||
| import time | |||
| import fnmatch | |||
| import tempfile | |||
| import tarfile | |||
| from distutils import log | |||
| try: | |||
| from site import USER_SITE | |||
| except ImportError: | |||
| USER_SITE = None | |||
| try: | |||
| import subprocess | |||
| def _python_cmd(*args): | |||
| args = (sys.executable,) + args | |||
| return subprocess.call(args) == 0 | |||
| except ImportError: | |||
| # will be used for python 2.3 | |||
| def _python_cmd(*args): | |||
| args = (sys.executable,) + args | |||
| # quoting arguments if windows | |||
| if sys.platform == 'win32': | |||
| def quote(arg): | |||
| if ' ' in arg: | |||
| return '"%s"' % arg | |||
| return arg | |||
| args = [quote(arg) for arg in args] | |||
| return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 | |||
| DEFAULT_VERSION = "0.6.14" | |||
| DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" | |||
| SETUPTOOLS_FAKED_VERSION = "0.6c11" | |||
| SETUPTOOLS_PKG_INFO = """\ | |||
| Metadata-Version: 1.0 | |||
| Name: setuptools | |||
| Version: %s | |||
| Summary: xxxx | |||
| Home-page: xxx | |||
| Author: xxx | |||
| Author-email: xxx | |||
| License: xxx | |||
| Description: xxx | |||
| """ % SETUPTOOLS_FAKED_VERSION | |||
| def _install(tarball): | |||
| # extracting the tarball | |||
| tmpdir = tempfile.mkdtemp() | |||
| log.warn('Extracting in %s', tmpdir) | |||
| old_wd = os.getcwd() | |||
| try: | |||
| os.chdir(tmpdir) | |||
| tar = tarfile.open(tarball) | |||
| _extractall(tar) | |||
| tar.close() | |||
| # going in the directory | |||
| subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) | |||
| os.chdir(subdir) | |||
| log.warn('Now working in %s', subdir) | |||
| # installing | |||
| log.warn('Installing Distribute') | |||
| if not _python_cmd('setup.py', 'install'): | |||
| log.warn('Something went wrong during the installation.') | |||
| log.warn('See the error message above.') | |||
| finally: | |||
| os.chdir(old_wd) | |||
| def _build_egg(egg, tarball, to_dir): | |||
| # extracting the tarball | |||
| tmpdir = tempfile.mkdtemp() | |||
| log.warn('Extracting in %s', tmpdir) | |||
| old_wd = os.getcwd() | |||
| try: | |||
| os.chdir(tmpdir) | |||
| tar = tarfile.open(tarball) | |||
| _extractall(tar) | |||
| tar.close() | |||
| # going in the directory | |||
| subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) | |||
| os.chdir(subdir) | |||
| log.warn('Now working in %s', subdir) | |||
| # building an egg | |||
| log.warn('Building a Distribute egg in %s', to_dir) | |||
| _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) | |||
| finally: | |||
| os.chdir(old_wd) | |||
| # returning the result | |||
| log.warn(egg) | |||
| if not os.path.exists(egg): | |||
| raise IOError('Could not build the egg.') | |||
| def _do_download(version, download_base, to_dir, download_delay): | |||
| egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' | |||
| % (version, sys.version_info[0], sys.version_info[1])) | |||
| if not os.path.exists(egg): | |||
| tarball = download_setuptools(version, download_base, | |||
| to_dir, download_delay) | |||
| _build_egg(egg, tarball, to_dir) | |||
| sys.path.insert(0, egg) | |||
| import setuptools | |||
| setuptools.bootstrap_install_from = egg | |||
| def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, | |||
| to_dir=os.curdir, download_delay=15, no_fake=True): | |||
| # making sure we use the absolute path | |||
| to_dir = os.path.abspath(to_dir) | |||
| was_imported = 'pkg_resources' in sys.modules or \ | |||
| 'setuptools' in sys.modules | |||
| try: | |||
| try: | |||
| import pkg_resources | |||
| if not hasattr(pkg_resources, '_distribute'): | |||
| if not no_fake: | |||
| _fake_setuptools() | |||
| raise ImportError | |||
| except ImportError: | |||
| return _do_download(version, download_base, to_dir, download_delay) | |||
| try: | |||
| pkg_resources.require("distribute>="+version) | |||
| return | |||
| except pkg_resources.VersionConflict: | |||
| e = sys.exc_info()[1] | |||
| if was_imported: | |||
| sys.stderr.write( | |||
| "The required version of distribute (>=%s) is not available,\n" | |||
| "and can't be installed while this script is running. Please\n" | |||
| "install a more recent version first, using\n" | |||
| "'easy_install -U distribute'." | |||
| "\n\n(Currently using %r)\n" % (version, e.args[0])) | |||
| sys.exit(2) | |||
| else: | |||
| del pkg_resources, sys.modules['pkg_resources'] # reload ok | |||
| return _do_download(version, download_base, to_dir, | |||
| download_delay) | |||
| except pkg_resources.DistributionNotFound: | |||
| return _do_download(version, download_base, to_dir, | |||
| download_delay) | |||
| finally: | |||
| if not no_fake: | |||
| _create_fake_setuptools_pkg_info(to_dir) | |||
| def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, | |||
| to_dir=os.curdir, delay=15): | |||
| """Download distribute from a specified location and return its filename | |||
| `version` should be a valid distribute version number that is available | |||
| as an egg for download under the `download_base` URL (which should end | |||
| with a '/'). `to_dir` is the directory where the egg will be downloaded. | |||
| `delay` is the number of seconds to pause before an actual download | |||
| attempt. | |||
| """ | |||
| # making sure we use the absolute path | |||
| to_dir = os.path.abspath(to_dir) | |||
| try: | |||
| from urllib.request import urlopen | |||
| except ImportError: | |||
| from urllib2 import urlopen | |||
| tgz_name = "distribute-%s.tar.gz" % version | |||
| url = download_base + tgz_name | |||
| saveto = os.path.join(to_dir, tgz_name) | |||
| src = dst = None | |||
| if not os.path.exists(saveto): # Avoid repeated downloads | |||
| try: | |||
| log.warn("Downloading %s", url) | |||
| src = urlopen(url) | |||
| # Read/write all in one block, so we don't create a corrupt file | |||
| # if the download is interrupted. | |||
| data = src.read() | |||
| dst = open(saveto, "wb") | |||
| dst.write(data) | |||
| finally: | |||
| if src: | |||
| src.close() | |||
| if dst: | |||
| dst.close() | |||
| return os.path.realpath(saveto) | |||
| def _no_sandbox(function): | |||
| def __no_sandbox(*args, **kw): | |||
| try: | |||
| from setuptools.sandbox import DirectorySandbox | |||
| if not hasattr(DirectorySandbox, '_old'): | |||
| def violation(*args): | |||
| pass | |||
| DirectorySandbox._old = DirectorySandbox._violation | |||
| DirectorySandbox._violation = violation | |||
| patched = True | |||
| else: | |||
| patched = False | |||
| except ImportError: | |||
| patched = False | |||
| try: | |||
| return function(*args, **kw) | |||
| finally: | |||
| if patched: | |||
| DirectorySandbox._violation = DirectorySandbox._old | |||
| del DirectorySandbox._old | |||
| return __no_sandbox | |||
| def _patch_file(path, content): | |||
| """Will backup the file then patch it""" | |||
| existing_content = open(path).read() | |||
| if existing_content == content: | |||
| # already patched | |||
| log.warn('Already patched.') | |||
| return False | |||
| log.warn('Patching...') | |||
| _rename_path(path) | |||
| f = open(path, 'w') | |||
| try: | |||
| f.write(content) | |||
| finally: | |||
| f.close() | |||
| return True | |||
| _patch_file = _no_sandbox(_patch_file) | |||
| def _same_content(path, content): | |||
| return open(path).read() == content | |||
| def _rename_path(path): | |||
| new_name = path + '.OLD.%s' % time.time() | |||
| log.warn('Renaming %s into %s', path, new_name) | |||
| os.rename(path, new_name) | |||
| return new_name | |||
| def _remove_flat_installation(placeholder): | |||
| if not os.path.isdir(placeholder): | |||
| log.warn('Unkown installation at %s', placeholder) | |||
| return False | |||
| found = False | |||
| for file in os.listdir(placeholder): | |||
| if fnmatch.fnmatch(file, 'setuptools*.egg-info'): | |||
| found = True | |||
| break | |||
| if not found: | |||
| log.warn('Could not locate setuptools*.egg-info') | |||
| return | |||
| log.warn('Removing elements out of the way...') | |||
| pkg_info = os.path.join(placeholder, file) | |||
| if os.path.isdir(pkg_info): | |||
| patched = _patch_egg_dir(pkg_info) | |||
| else: | |||
| patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) | |||
| if not patched: | |||
| log.warn('%s already patched.', pkg_info) | |||
| return False | |||
| # now let's move the files out of the way | |||
| for element in ('setuptools', 'pkg_resources.py', 'site.py'): | |||
| element = os.path.join(placeholder, element) | |||
| if os.path.exists(element): | |||
| _rename_path(element) | |||
| else: | |||
| log.warn('Could not find the %s element of the ' | |||
| 'Setuptools distribution', element) | |||
| return True | |||
| _remove_flat_installation = _no_sandbox(_remove_flat_installation) | |||
| def _after_install(dist): | |||
| log.warn('After install bootstrap.') | |||
| placeholder = dist.get_command_obj('install').install_purelib | |||
| _create_fake_setuptools_pkg_info(placeholder) | |||
| def _create_fake_setuptools_pkg_info(placeholder): | |||
| if not placeholder or not os.path.exists(placeholder): | |||
| log.warn('Could not find the install location') | |||
| return | |||
| pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) | |||
| setuptools_file = 'setuptools-%s-py%s.egg-info' % \ | |||
| (SETUPTOOLS_FAKED_VERSION, pyver) | |||
| pkg_info = os.path.join(placeholder, setuptools_file) | |||
| if os.path.exists(pkg_info): | |||
| log.warn('%s already exists', pkg_info) | |||
| return | |||
| log.warn('Creating %s', pkg_info) | |||
| f = open(pkg_info, 'w') | |||
| try: | |||
| f.write(SETUPTOOLS_PKG_INFO) | |||
| finally: | |||
| f.close() | |||
| pth_file = os.path.join(placeholder, 'setuptools.pth') | |||
| log.warn('Creating %s', pth_file) | |||
| f = open(pth_file, 'w') | |||
| try: | |||
| f.write(os.path.join(os.curdir, setuptools_file)) | |||
| finally: | |||
| f.close() | |||
| _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) | |||
| def _patch_egg_dir(path): | |||
| # let's check if it's already patched | |||
| pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') | |||
| if os.path.exists(pkg_info): | |||
| if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): | |||
| log.warn('%s already patched.', pkg_info) | |||
| return False | |||
| _rename_path(path) | |||
| os.mkdir(path) | |||
| os.mkdir(os.path.join(path, 'EGG-INFO')) | |||
| pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') | |||
| f = open(pkg_info, 'w') | |||
| try: | |||
| f.write(SETUPTOOLS_PKG_INFO) | |||
| finally: | |||
| f.close() | |||
| return True | |||
| _patch_egg_dir = _no_sandbox(_patch_egg_dir) | |||
| def _before_install(): | |||
| log.warn('Before install bootstrap.') | |||
| _fake_setuptools() | |||
| def _under_prefix(location): | |||
| if 'install' not in sys.argv: | |||
| return True | |||
| args = sys.argv[sys.argv.index('install')+1:] | |||
| for index, arg in enumerate(args): | |||
| for option in ('--root', '--prefix'): | |||
| if arg.startswith('%s=' % option): | |||
| top_dir = arg.split('root=')[-1] | |||
| return location.startswith(top_dir) | |||
| elif arg == option: | |||
| if len(args) > index: | |||
| top_dir = args[index+1] | |||
| return location.startswith(top_dir) | |||
| if arg == '--user' and USER_SITE is not None: | |||
| return location.startswith(USER_SITE) | |||
| return True | |||
| def _fake_setuptools(): | |||
| log.warn('Scanning installed packages') | |||
| try: | |||
| import pkg_resources | |||
| except ImportError: | |||
| # we're cool | |||
| log.warn('Setuptools or Distribute does not seem to be installed.') | |||
| return | |||
| ws = pkg_resources.working_set | |||
| try: | |||
| setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', | |||
| replacement=False)) | |||
| except TypeError: | |||
| # old distribute API | |||
| setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) | |||
| if setuptools_dist is None: | |||
| log.warn('No setuptools distribution found') | |||
| return | |||
| # detecting if it was already faked | |||
| setuptools_location = setuptools_dist.location | |||
| log.warn('Setuptools installation detected at %s', setuptools_location) | |||
| # if --root or --preix was provided, and if | |||
| # setuptools is not located in them, we don't patch it | |||
| if not _under_prefix(setuptools_location): | |||
| log.warn('Not patching, --root or --prefix is installing Distribute' | |||
| ' in another location') | |||
| return | |||
| # let's see if its an egg | |||
| if not setuptools_location.endswith('.egg'): | |||
| log.warn('Non-egg installation') | |||
| res = _remove_flat_installation(setuptools_location) | |||
| if not res: | |||
| return | |||
| else: | |||
| log.warn('Egg installation') | |||
| pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') | |||
| if (os.path.exists(pkg_info) and | |||
| _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): | |||
| log.warn('Already patched.') | |||
| return | |||
| log.warn('Patching...') | |||
| # let's create a fake egg replacing setuptools one | |||
| res = _patch_egg_dir(setuptools_location) | |||
| if not res: | |||
| return | |||
| log.warn('Patched done.') | |||
| _relaunch() | |||
| def _relaunch(): | |||
| log.warn('Relaunching...') | |||
| # we have to relaunch the process | |||
| # pip marker to avoid a relaunch bug | |||
| if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: | |||
| sys.argv[0] = 'setup.py' | |||
| args = [sys.executable] + sys.argv | |||
| sys.exit(subprocess.call(args)) | |||
| def _extractall(self, path=".", members=None): | |||
| """Extract all members from the archive to the current working | |||
| directory and set owner, modification time and permissions on | |||
| directories afterwards. `path' specifies a different directory | |||
| to extract to. `members' is optional and must be a subset of the | |||
| list returned by getmembers(). | |||
| """ | |||
| import copy | |||
| import operator | |||
| from tarfile import ExtractError | |||
| directories = [] | |||
| if members is None: | |||
| members = self | |||
| for tarinfo in members: | |||
| if tarinfo.isdir(): | |||
| # Extract directories with a safe mode. | |||
| directories.append(tarinfo) | |||
| tarinfo = copy.copy(tarinfo) | |||
| tarinfo.mode = 448 # decimal for oct 0700 | |||
| self.extract(tarinfo, path) | |||
| # Reverse sort directories. | |||
| if sys.version_info < (2, 4): | |||
| def sorter(dir1, dir2): | |||
| return cmp(dir1.name, dir2.name) | |||
| directories.sort(sorter) | |||
| directories.reverse() | |||
| else: | |||
| directories.sort(key=operator.attrgetter('name'), reverse=True) | |||
| # Set correct owner, mtime and filemode on directories. | |||
| for tarinfo in directories: | |||
| dirpath = os.path.join(path, tarinfo.name) | |||
| try: | |||
| self.chown(tarinfo, dirpath) | |||
| self.utime(tarinfo, dirpath) | |||
| self.chmod(tarinfo, dirpath) | |||
| except ExtractError: | |||
| e = sys.exc_info()[1] | |||
| if self.errorlevel > 1: | |||
| raise | |||
| else: | |||
| self._dbg(1, "tarfile: %s" % e) | |||
| def main(argv, version=DEFAULT_VERSION): | |||
| """Install or upgrade setuptools and EasyInstall""" | |||
| tarball = download_setuptools() | |||
| _install(tarball) | |||
| if __name__ == '__main__': | |||
| main(sys.argv[1:]) | |||
| @@ -20,6 +20,9 @@ class AutoExtendPlugin(Plugin): | |||
| and there is no extends statement, this plugin automatically adds | |||
| an extends statement to the top of the file. | |||
| """ | |||
| if not resource.uses_template: | |||
| return text | |||
| layout = None | |||
| block = None | |||
| try: | |||
| @@ -7,7 +7,7 @@ import re | |||
| from hyde.model import Expando | |||
| from hyde.plugin import Plugin | |||
| from hyde.site import Node, Resource | |||
| from hyde.util import add_method, pairwalk | |||
| from hyde.util import add_method, add_property, pairwalk | |||
| from collections import namedtuple | |||
| from functools import partial | |||
| @@ -42,6 +42,10 @@ class Group(Expando): | |||
| 'walk_resources_grouped_by_%s' % self.name, | |||
| Group.walk_resources, | |||
| group=self) | |||
| add_property(Resource, | |||
| '%s_group' % self.name, | |||
| Group.get_resource_group, | |||
| group=self) | |||
| add_method(Resource, | |||
| 'walk_%s_groups' % self.name, | |||
| Group.walk_resource_groups, | |||
| @@ -57,6 +61,23 @@ class Group(Expando): | |||
| else: | |||
| return super(Group, self).set_expando(key, value) | |||
| @staticmethod | |||
| def get_resource_group(resource, group): | |||
| """ | |||
| This method gets attached to the resource object. | |||
| Returns group and its ancestors that the resource | |||
| belongs to, in that order. | |||
| """ | |||
| try: | |||
| group_name = getattr(resource.meta, group.root.name) | |||
| except AttributeError: | |||
| group_name = None | |||
| return next((g for g in group.walk_groups() | |||
| if g.name == group_name), None) \ | |||
| if group_name \ | |||
| else None | |||
| @staticmethod | |||
| def walk_resource_groups(resource, group): | |||
| """ | |||
| @@ -4,6 +4,7 @@ Jinja template utilties | |||
| """ | |||
| from hyde.fs import File, Folder | |||
| from hyde.model import Expando | |||
| from hyde.template import HtmlWrap, Template | |||
| from hyde.site import Resource | |||
| from hyde.util import getLoggerWithNullHandler, getLoggerWithConsoleHandler | |||
| @@ -58,11 +59,12 @@ def markdown(env, value): | |||
| d = {} | |||
| if hasattr(env.config, 'markdown'): | |||
| d['extensions'] = getattr(env.config.markdown, 'extensions', []) | |||
| d['extension_configs'] = getattr(env.config.markdown, 'extension_configs', {}) | |||
| d['extension_configs'] = getattr(env.config.markdown, | |||
| 'extension_configs', | |||
| Expando({})).to_dict() | |||
| md = markdown.Markdown(**d) | |||
| return md.convert(output) | |||
| @environmentfilter | |||
| def syntax(env, value, lexer=None, filename=None): | |||
| """ | |||
| @@ -81,7 +83,9 @@ def syntax(env, value, lexer=None, filename=None): | |||
| lexers.guess_lexer(value)) | |||
| settings = {} | |||
| if hasattr(env.config, 'syntax'): | |||
| settings = getattr(env.config.syntax, 'options', {}) | |||
| settings = getattr(env.config.syntax, | |||
| 'options', | |||
| Expando({})).to_dict() | |||
| formatter = formatters.HtmlFormatter(**settings) | |||
| code = pygments.highlight(value, pyg, formatter) | |||
| @@ -117,6 +121,49 @@ class Markdown(Extension): | |||
| output = caller().strip() | |||
| return markdown(self.environment, output) | |||
| class YamlVar(Extension): | |||
| """ | |||
| An extension that converts the content between the tags | |||
| into an yaml object and sets the value in the given | |||
| variable. | |||
| """ | |||
| tags = set(['yaml']) | |||
| def parse(self, parser): | |||
| """ | |||
| Parses the contained data and defers to the callback to load it as | |||
| yaml. | |||
| """ | |||
| lineno = parser.stream.next().lineno | |||
| var = parser.stream.expect('name').value | |||
| body = parser.parse_statements(['name:endyaml'], drop_needle=True) | |||
| return [ | |||
| nodes.Assign( | |||
| nodes.Name(var, 'store'), | |||
| nodes.Const({}) | |||
| ).set_lineno(lineno), | |||
| nodes.CallBlock( | |||
| self.call_method('_set_yaml', args=[nodes.Name(var, 'load')]), | |||
| [], [], body).set_lineno(lineno) | |||
| ] | |||
| def _set_yaml(self, var, caller=None): | |||
| """ | |||
| Loads the yaml data into the specified variable. | |||
| """ | |||
| if not caller: | |||
| return '' | |||
| try: | |||
| import yaml | |||
| except ImportError: | |||
| return '' | |||
| out = caller().strip() | |||
| var.update(yaml.load(out)) | |||
| return '' | |||
| def parse_kwargs(parser): | |||
| name = parser.stream.expect('name').value | |||
| parser.stream.expect('assign') | |||
| @@ -265,7 +312,13 @@ class Refer(Extension): | |||
| includeNode.ignore_missing = False | |||
| includeNode.template = template | |||
| temp = parser.free_identifier(lineno) | |||
| return [ | |||
| nodes.Assign( | |||
| nodes.Name(temp.name, 'store'), | |||
| nodes.Name(MARKINGS, 'load') | |||
| ).set_lineno(lineno), | |||
| nodes.Assign( | |||
| nodes.Name(MARKINGS, 'store'), | |||
| nodes.Const({})).set_lineno(lineno), | |||
| @@ -295,6 +348,10 @@ class Refer(Extension): | |||
| nodes.Getitem(nodes.Name(namespace, 'load'), | |||
| nodes.Const('parent_resource'), 'load') | |||
| ).set_lineno(lineno), | |||
| nodes.Assign( | |||
| nodes.Name(MARKINGS, 'store'), | |||
| nodes.Name(temp.name, 'load') | |||
| ).set_lineno(lineno), | |||
| ] | |||
| def _push_resource(self, namespace, site, resource, template, caller): | |||
| @@ -378,6 +435,7 @@ class Jinja2Template(Template): | |||
| Syntax, | |||
| Reference, | |||
| Refer, | |||
| YamlVar, | |||
| 'jinja2.ext.do', | |||
| 'jinja2.ext.loopcontrols', | |||
| 'jinja2.ext.with_']) | |||
| @@ -13,7 +13,7 @@ | |||
| <!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame | |||
| Remove this if you use the .htaccess --> | |||
| <meta http-equiv="X-UA-Compatible" content="{{page.meta.compatibility|default('IE=edge,chrome=1')"> | |||
| <meta http-equiv="X-UA-Compatible" content="{{page.meta.compatibility|default('IE=edge,chrome=1')}}"> | |||
| <!-- encoding must be specified within the first 512 bytes www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#charset --> | |||
| @@ -25,7 +25,7 @@ | |||
| <meta name="author" content="{{resource.meta.author}}"> | |||
| <!-- Mobile viewport optimized: j.mp/bplateviewport --> | |||
| <meta name="viewport" content="{{page.meta.viewport|default('width=device-width, initial-scale=1.0')"> | |||
| <meta name="viewport" content="{{page.meta.viewport|default('width=device-width, initial-scale=1.0')}}"> | |||
| {% block favicons %} | |||
| <!-- Place favicon.ico & apple-touch-icon.png in the root of your domain and delete these references --> | |||
| @@ -99,4 +99,4 @@ | |||
| {% endblock js %} | |||
| </body> | |||
| </html> | |||
| {% endblock all %} | |||
| {% endblock all %} | |||
| @@ -57,6 +57,16 @@ class Expando(object): | |||
| else: | |||
| return primitive | |||
| def to_dict(self): | |||
| """ | |||
| Reverse transform an expando to dict | |||
| """ | |||
| d = self.__dict__ | |||
| for k, v in d.iteritems(): | |||
| if isinstance(v, Expando): | |||
| d[k] = v.to_dict() | |||
| return d | |||
| class Context(object): | |||
| """ | |||
| @@ -58,9 +58,13 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||
| logger.debug("Trying to load file based on request:[%s]" % result.path) | |||
| path = result.path.lstrip('/') | |||
| if path.strip() == "" or File(path).kind.strip() == "": | |||
| return site.config.deploy_root_path.child(path) | |||
| res = site.content.resource_from_relative_deploy_path(path) | |||
| deployed = site.config.deploy_root_path.child(path) | |||
| deployed = Folder.file_or_folder(deployed) | |||
| if isinstance(deployed, Folder): | |||
| node = site.content.node_from_relative_path(path) | |||
| res = node.get_resource('index.html') | |||
| else: | |||
| res = site.content.resource_from_relative_deploy_path(path) | |||
| if not res: | |||
| @@ -130,6 +130,19 @@ class TestGrouperSingleLevel(object): | |||
| plugin_resources = [resource.name for resource in self.s.content.walk_resources_grouped_by_plugins()] | |||
| assert plugin_resources == self.plugins | |||
| def test_resource_group(self): | |||
| groups = dict([(g.name, g) for g in self.s.grouper['section'].groups]) | |||
| for name, group in groups.items(): | |||
| pages = getattr(self, name) | |||
| for page in pages: | |||
| res = self.s.content.resource_from_relative_path('blog/' + page) | |||
| assert hasattr(res, 'section_group') | |||
| res_group = getattr(res, 'section_group') | |||
| print "%s, %s=%s" % (page, group.name, res_group.name) | |||
| assert res_group == group | |||
| def test_resource_belongs_to(self): | |||
| groups = dict([(g.name, g) for g in self.s.grouper['section'].groups]) | |||
| @@ -128,6 +128,7 @@ def assert_markdown_typogrify_processed_well(include_text, includer_text): | |||
| gen.load_template_if_needed() | |||
| template = gen.template | |||
| html = template.render(includer_text, {}).strip() | |||
| print html | |||
| assert html | |||
| q = PyQuery(html) | |||
| assert "is_processable" not in html | |||
| @@ -200,7 +201,7 @@ class TestJinjaTemplate(object): | |||
| deps = list(t.get_dependencies('inc.md')) | |||
| assert len(deps) == 1 | |||
| assert not deps[0] | |||
| @@ -314,6 +315,52 @@ Hyde & Jinja. | |||
| assert "mark" not in html | |||
| assert "reference" not in html | |||
| def test_two_level_refer_with_var(self): | |||
| text = """ | |||
| === | |||
| is_processable: False | |||
| === | |||
| <div class="fulltext"> | |||
| {% filter markdown|typogrify %} | |||
| {% mark heading %} | |||
| This is a heading | |||
| ================= | |||
| {% endmark %} | |||
| {% reference content %} | |||
| Hyde & Jinja. | |||
| {% endreference %} | |||
| {% endfilter %} | |||
| </div> | |||
| """ | |||
| text2 = """ | |||
| {% set super = 'super.md' %} | |||
| {% refer to super as sup %} | |||
| <div class="justhead"> | |||
| {% mark child %} | |||
| {{ sup.heading }} | |||
| {% endmark %} | |||
| {% mark cont %} | |||
| {{ sup.content }} | |||
| {% endmark %} | |||
| </div> | |||
| """ | |||
| text3 = """ | |||
| {% set incu = 'inc.md' %} | |||
| {% refer to incu as inc %} | |||
| {% filter markdown|typogrify %} | |||
| {{ inc.child }} | |||
| {{ inc.cont }} | |||
| {% endfilter %} | |||
| """ | |||
| superinc = File(TEST_SITE.child('content/super.md')) | |||
| superinc.write(text) | |||
| html = assert_markdown_typogrify_processed_well(text2, text3) | |||
| assert "mark" not in html | |||
| assert "reference" not in html | |||
| def test_refer_with_var(self): | |||
| text = """ | |||
| @@ -340,4 +387,45 @@ Hyde & Jinja. | |||
| """ | |||
| html = assert_markdown_typogrify_processed_well(text, text2) | |||
| assert "mark" not in html | |||
| assert "reference" not in html | |||
| assert "reference" not in html | |||
| def test_yaml_tag(salf): | |||
| text = """ | |||
| {% yaml test %} | |||
| one: | |||
| - A | |||
| - B | |||
| - C | |||
| two: | |||
| - D | |||
| - E | |||
| - F | |||
| {% endyaml %} | |||
| {% for section, values in test.items() %} | |||
| <ul class="{{ section }}"> | |||
| {% for value in values %} | |||
| <li>{{ value }}</li> | |||
| {% endfor %} | |||
| </ul> | |||
| {% endfor %} | |||
| """ | |||
| t = Jinja2Template(JINJA2.path) | |||
| t.configure(None) | |||
| html = t.render(text, {}).strip() | |||
| actual = PyQuery(html) | |||
| assert actual("ul").length == 2 | |||
| assert actual("ul.one").length == 1 | |||
| assert actual("ul.two").length == 1 | |||
| assert actual("li").length == 6 | |||
| assert actual("ul.one li").length == 3 | |||
| assert actual("ul.two li").length == 3 | |||
| ones = [item.text for item in actual("ul.one li")] | |||
| assert ones == ["A", "B", "C"] | |||
| twos = [item.text for item in actual("ul.two li")] | |||
| assert twos == ["D", "E", "F"] | |||
| @@ -43,6 +43,26 @@ def test_expando_update(): | |||
| assert x.a == 789 | |||
| assert x.f == "opq" | |||
| def test_expando_to_dict(): | |||
| d = {"a": 123, "b": {"c": 456, "d": {"e": "abc"}}} | |||
| x = Expando(d) | |||
| assert d == x.to_dict() | |||
| def test_expando_to_dict_with_update(): | |||
| d1 = {"a": 123, "b": "abc"} | |||
| x = Expando(d1) | |||
| d = {"b": {"c": 456, "d": {"e": "abc"}}, "f": "lmn"} | |||
| x.update(d) | |||
| expected = {} | |||
| expected.update(d1) | |||
| expected.update(d) | |||
| assert expected == x.to_dict() | |||
| d2 = {"a": 789, "f": "opq"} | |||
| y = Expando(d2) | |||
| x.update(y) | |||
| expected.update(d2) | |||
| assert expected == x.to_dict() | |||
| TEST_SITE = File(__file__).parent.child_folder('_test') | |||
| import yaml | |||
| @@ -92,11 +92,16 @@ def make_method(method_name, method_): | |||
| method__.__name__ = method_name | |||
| return method__ | |||
| def add_property(obj, method_name, method_, *args, **kwargs): | |||
| from functools import partial | |||
| m = make_method(method_name, partial(method_, *args, **kwargs)) | |||
| setattr(obj, method_name, property(m)) | |||
| def add_method(obj, method_name, method_, *args, **kwargs): | |||
| from functools import partial | |||
| m = make_method(method_name, partial(method_, *args, **kwargs)) | |||
| setattr(obj, method_name, m) | |||
| def pairwalk(iterable): | |||
| a, b = tee(iterable) | |||
| next(b, None) | |||
| @@ -3,4 +3,4 @@ | |||
| Handles hyde version | |||
| TODO: Use fabric like versioning scheme | |||
| """ | |||
| __version__ = '0.6.0' | |||
| __version__ = '0.7b1' | |||
| @@ -1,2 +0,0 @@ | |||
| argparse | |||
| -r req-2.7.txt | |||
| @@ -1,7 +0,0 @@ | |||
| commando==0.1.1a | |||
| PyYAML==3.09 | |||
| Markdown==2.0.3 | |||
| MarkupSafe==0.11 | |||
| smartypants==1.6.0.3 | |||
| -e git://github.com/hyde/typogrify.git#egg=typogrify | |||
| Jinja2==2.5.5 | |||
| @@ -1 +1,8 @@ | |||
| -r req-2.6.txt | |||
| argparse | |||
| commando | |||
| PyYAML | |||
| Markdown | |||
| MarkupSafe | |||
| smartypants | |||
| -e git://github.com/hyde/typogrify.git#egg=typogrify | |||
| Jinja2 | |||
| @@ -1,13 +1,123 @@ | |||
| # Bootstrap installation of Distribute | |||
| import distribute_setup | |||
| distribute_setup.use_setuptools() | |||
| from setuptools import setup, find_packages | |||
| from hyde.version import __version__ | |||
| setup(name='hyde', | |||
| from distutils.util import convert_path | |||
| from fnmatch import fnmatchcase | |||
| import os | |||
| import sys | |||
| PROJECT = 'hyde' | |||
| try: | |||
| long_description = open('README.markdown', 'rt').read() | |||
| except IOError: | |||
| long_description = '' | |||
| ################################################################################ | |||
| # find_package_data is an Ian Bicking creation. | |||
| # Provided as an attribute, so you can append to these instead | |||
| # of replicating them: | |||
| standard_exclude = ('*.py', '*.pyc', '*~', '.*', '*.bak', '*.swp*') | |||
| standard_exclude_directories = ('.*', 'CVS', '_darcs', './build', | |||
| './dist', 'EGG-INFO', '*.egg-info') | |||
| def find_package_data( | |||
| where='.', package='', | |||
| exclude=standard_exclude, | |||
| exclude_directories=standard_exclude_directories, | |||
| only_in_packages=True, | |||
| show_ignored=False): | |||
| """ | |||
| Return a dictionary suitable for use in ``package_data`` | |||
| in a distutils ``setup.py`` file. | |||
| The dictionary looks like:: | |||
| {'package': [files]} | |||
| Where ``files`` is a list of all the files in that package that | |||
| don't match anything in ``exclude``. | |||
| If ``only_in_packages`` is true, then top-level directories that | |||
| are not packages won't be included (but directories under packages | |||
| will). | |||
| Directories matching any pattern in ``exclude_directories`` will | |||
| be ignored; by default directories with leading ``.``, ``CVS``, | |||
| and ``_darcs`` will be ignored. | |||
| If ``show_ignored`` is true, then all the files that aren't | |||
| included in package data are shown on stderr (for debugging | |||
| purposes). | |||
| Note patterns use wildcards, or can be exact paths (including | |||
| leading ``./``), and all searching is case-insensitive. | |||
| This function is by Ian Bicking. | |||
| """ | |||
| out = {} | |||
| stack = [(convert_path(where), '', package, only_in_packages)] | |||
| while stack: | |||
| where, prefix, package, only_in_packages = stack.pop(0) | |||
| for name in os.listdir(where): | |||
| fn = os.path.join(where, name) | |||
| if os.path.isdir(fn): | |||
| bad_name = False | |||
| for pattern in exclude_directories: | |||
| if (fnmatchcase(name, pattern) | |||
| or fn.lower() == pattern.lower()): | |||
| bad_name = True | |||
| if show_ignored: | |||
| print >> sys.stderr, ( | |||
| "Directory %s ignored by pattern %s" | |||
| % (fn, pattern)) | |||
| break | |||
| if bad_name: | |||
| continue | |||
| if os.path.isfile(os.path.join(fn, '__init__.py')): | |||
| if not package: | |||
| new_package = name | |||
| else: | |||
| new_package = package + '.' + name | |||
| stack.append((fn, '', new_package, False)) | |||
| else: | |||
| stack.append((fn, prefix + name + '/', package, only_in_packages)) | |||
| elif package or not only_in_packages: | |||
| # is a file | |||
| bad_name = False | |||
| for pattern in exclude: | |||
| if (fnmatchcase(name, pattern) | |||
| or fn.lower() == pattern.lower()): | |||
| bad_name = True | |||
| if show_ignored: | |||
| print >> sys.stderr, ( | |||
| "File %s ignored by pattern %s" | |||
| % (fn, pattern)) | |||
| break | |||
| if bad_name: | |||
| continue | |||
| out.setdefault(package, []).append(prefix+name) | |||
| return out | |||
| ################################################################################ | |||
| setup(name=PROJECT, | |||
| version=__version__, | |||
| description='hyde is a pythonic static website generator', | |||
| description='hyde is a static website generator', | |||
| long_description = long_description, | |||
| author='Lakshmi Vyas', | |||
| author_email='lakshmi.vyas@gmail.com', | |||
| url='http://ringce.com/hyde', | |||
| packages=find_packages(), | |||
| dependency_links=[ | |||
| "https://github.com/hyde/typogrify/tarball/hyde-setup#egg=typogrify-hyde" | |||
| ], | |||
| install_requires=( | |||
| 'argparse', | |||
| 'commando', | |||
| @@ -15,8 +125,21 @@ setup(name='hyde', | |||
| 'pyYAML', | |||
| 'markdown', | |||
| 'smartypants', | |||
| 'pygments' | |||
| 'pygments', | |||
| 'typogrify-hyde' | |||
| ), | |||
| tests_require=( | |||
| 'nose', | |||
| ), | |||
| test_suite='nose.collector', | |||
| include_package_data = True, | |||
| # Scan the input for package information | |||
| # to grab any data files (text, images, etc.) | |||
| # associated with sub-packages. | |||
| package_data = find_package_data(PROJECT, | |||
| package=PROJECT, | |||
| only_in_packages=False, | |||
| ), | |||
| scripts=['main.py'], | |||
| entry_points={ | |||
| 'console_scripts': [ | |||
| @@ -25,7 +148,7 @@ setup(name='hyde', | |||
| }, | |||
| license='MIT', | |||
| classifiers=[ | |||
| 'Development Status :: 3 - Alpha', | |||
| 'Development Status :: 4 - Beta', | |||
| 'Environment :: Console', | |||
| 'Intended Audience :: End Users/Desktop', | |||
| 'Intended Audience :: Developers', | |||
| @@ -34,11 +157,13 @@ setup(name='hyde', | |||
| 'Operating System :: MacOS :: MacOS X', | |||
| 'Operating System :: Unix', | |||
| 'Operating System :: POSIX', | |||
| 'Operating System :: Microsoft :: Windows', | |||
| 'Programming Language :: Python', | |||
| 'Programming Language :: Python :: 2.7', | |||
| 'Topic :: Software Development', | |||
| 'Topic :: Software Development :: Build Tools', | |||
| 'Topic :: Software Development :: Websites', | |||
| 'Topic :: Software Development :: Static Websites', | |||
| 'Topic :: Software Development :: Code Generators', | |||
| 'Topic :: Internet', | |||
| 'Topic :: Internet :: WWW/HTTP :: Site Management', | |||
| ], | |||
| zip_safe=False, | |||
| ) | |||