From fc678e423e0de6cb7d23430421d50de8d5dac3c9 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Sat, 5 Feb 2011 15:52:00 +0530 Subject: [PATCH 01/13] Excluded non-processable resoucrces from auto extend --- hyde/ext/plugins/auto_extend.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hyde/ext/plugins/auto_extend.py b/hyde/ext/plugins/auto_extend.py index 4017667..fe3b095 100644 --- a/hyde/ext/plugins/auto_extend.py +++ b/hyde/ext/plugins/auto_extend.py @@ -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.is_processable or not resource.uses_template: + return text layout = None block = None try: From 025f6092397b7777a8d902385f5128e99f0fcf6a Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Tue, 8 Feb 2011 16:03:00 +0530 Subject: [PATCH 02/13] Added support for multi level references --- hyde/ext/templates/jinja.py | 10 +++++++ hyde/tests/test_jinja2template.py | 49 ++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/hyde/ext/templates/jinja.py b/hyde/ext/templates/jinja.py index f3327e2..ed8e83b 100644 --- a/hyde/ext/templates/jinja.py +++ b/hyde/ext/templates/jinja.py @@ -265,7 +265,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 +301,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): diff --git a/hyde/tests/test_jinja2template.py b/hyde/tests/test_jinja2template.py index 27b6e1b..652cfff 100644 --- a/hyde/tests/test_jinja2template.py +++ b/hyde/tests/test_jinja2template.py @@ -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 +=== +
+{% filter markdown|typogrify %} +{% mark heading %} +This is a heading +================= +{% endmark %} +{% reference content %} +Hyde & Jinja. +{% endreference %} +{% endfilter %} +
+""" + + text2 = """ +{% set super = 'super.md' %} +{% refer to super as sup %} +
+{% mark child %} +{{ sup.heading }} +{% endmark %} +{% mark cont %} +{{ sup.content }} +{% endmark %} +
+""" + 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 = """ From 3bcd2904c2f56b4491663ea001d19114186b3c07 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Wed, 9 Feb 2011 17:41:33 +0530 Subject: [PATCH 03/13] Added ability to get the group associated with the resource --- hyde/ext/plugins/grouper.py | 23 ++++++++++++++++++++++- hyde/tests/ext/test_grouper.py | 13 +++++++++++++ hyde/util.py | 7 ++++++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/hyde/ext/plugins/grouper.py b/hyde/ext/plugins/grouper.py index 4162685..d07e1f5 100644 --- a/hyde/ext/plugins/grouper.py +++ b/hyde/ext/plugins/grouper.py @@ -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): """ diff --git a/hyde/tests/ext/test_grouper.py b/hyde/tests/ext/test_grouper.py index d215a4e..32886f1 100644 --- a/hyde/tests/ext/test_grouper.py +++ b/hyde/tests/ext/test_grouper.py @@ -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]) diff --git a/hyde/util.py b/hyde/util.py index 71f0f9b..e6bde93 100644 --- a/hyde/util.py +++ b/hyde/util.py @@ -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) From 88b701b813440c455d91e73d9b30efe3cb05dacf Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Wed, 9 Feb 2011 22:28:38 +0530 Subject: [PATCH 04/13] Added yaml extension for inline yaml in templates --- hyde/ext/templates/jinja.py | 45 ++++++++++++++++++++++++++++++- hyde/tests/test_jinja2template.py | 43 ++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/hyde/ext/templates/jinja.py b/hyde/ext/templates/jinja.py index ed8e83b..bfe2d1b 100644 --- a/hyde/ext/templates/jinja.py +++ b/hyde/ext/templates/jinja.py @@ -62,7 +62,6 @@ def markdown(env, value): md = markdown.Markdown(**d) return md.convert(output) - @environmentfilter def syntax(env, value, lexer=None, filename=None): """ @@ -117,6 +116,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') @@ -388,6 +430,7 @@ class Jinja2Template(Template): Syntax, Reference, Refer, + YamlVar, 'jinja2.ext.do', 'jinja2.ext.loopcontrols', 'jinja2.ext.with_']) diff --git a/hyde/tests/test_jinja2template.py b/hyde/tests/test_jinja2template.py index 652cfff..98eb1b5 100644 --- a/hyde/tests/test_jinja2template.py +++ b/hyde/tests/test_jinja2template.py @@ -387,4 +387,45 @@ Hyde & Jinja. """ html = assert_markdown_typogrify_processed_well(text, text2) assert "mark" not in html - assert "reference" not in html \ No newline at end of file + 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() %} +
    + {% for value in values %} +
  • {{ value }}
  • + {% endfor %} +
+{% 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"] \ No newline at end of file From cbb9e0e4ac7bb4b425b17a4bdb10713681f98c86 Mon Sep 17 00:00:00 2001 From: Tinnet Coronam Date: Sun, 13 Feb 2011 03:09:47 +0100 Subject: [PATCH 05/13] added missing closing double curly braces --- hyde/layouts/basic/layout/base.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hyde/layouts/basic/layout/base.html b/hyde/layouts/basic/layout/base.html index 9775224..88b5bbf 100644 --- a/hyde/layouts/basic/layout/base.html +++ b/hyde/layouts/basic/layout/base.html @@ -13,7 +13,7 @@ - + @@ -25,7 +25,7 @@ - + {% block favicons %} @@ -99,4 +99,4 @@ {% endblock js %} -{% endblock all %} \ No newline at end of file +{% endblock all %} From 685f3c7999d1f3d8ecaeb4e1827b77aa920272f1 Mon Sep 17 00:00:00 2001 From: Tinnet Coronam Date: Fri, 18 Feb 2011 19:17:16 +0100 Subject: [PATCH 06/13] pygments call with ** unpacking needs a dict --- hyde/ext/templates/jinja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyde/ext/templates/jinja.py b/hyde/ext/templates/jinja.py index bfe2d1b..c1e00ff 100644 --- a/hyde/ext/templates/jinja.py +++ b/hyde/ext/templates/jinja.py @@ -80,7 +80,7 @@ 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 = dict(getattr(env.config.syntax, 'options', {})) formatter = formatters.HtmlFormatter(**settings) code = pygments.highlight(value, pyg, formatter) From cb653deb367e86de9acccdecc48c4f40491f43dd Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Sat, 19 Feb 2011 12:10:45 +0530 Subject: [PATCH 07/13] Added to_dict method to expando --- hyde/ext/plugins/auto_extend.py | 2 +- hyde/model.py | 10 ++++++++++ hyde/tests/test_model.py | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/hyde/ext/plugins/auto_extend.py b/hyde/ext/plugins/auto_extend.py index fe3b095..e9a7564 100644 --- a/hyde/ext/plugins/auto_extend.py +++ b/hyde/ext/plugins/auto_extend.py @@ -21,7 +21,7 @@ class AutoExtendPlugin(Plugin): an extends statement to the top of the file. """ - if not resource.is_processable or not resource.uses_template: + if not resource.uses_template: return text layout = None block = None diff --git a/hyde/model.py b/hyde/model.py index 06269a7..0330f8c 100644 --- a/hyde/model.py +++ b/hyde/model.py @@ -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): """ diff --git a/hyde/tests/test_model.py b/hyde/tests/test_model.py index af45bf9..2c076d0 100644 --- a/hyde/tests/test_model.py +++ b/hyde/tests/test_model.py @@ -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 From f767b5b794b916ce7366e9b2e49735d376cae879 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Sat, 19 Feb 2011 12:23:23 +0530 Subject: [PATCH 08/13] Used the new to_dict method in expando --- hyde/ext/templates/jinja.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hyde/ext/templates/jinja.py b/hyde/ext/templates/jinja.py index c1e00ff..e5fa071 100644 --- a/hyde/ext/templates/jinja.py +++ b/hyde/ext/templates/jinja.py @@ -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,7 +59,9 @@ 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) @@ -80,7 +83,9 @@ def syntax(env, value, lexer=None, filename=None): lexers.guess_lexer(value)) settings = {} if hasattr(env.config, 'syntax'): - settings = dict(getattr(env.config.syntax, 'options', {})) + settings = getattr(env.config.syntax, + 'options', + Expando({})).to_dict() formatter = formatters.HtmlFormatter(**settings) code = pygments.highlight(value, pyg, formatter) From 8026d150ef1635789d66a264258135dce2c6ebf3 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Sun, 20 Feb 2011 16:34:48 +0530 Subject: [PATCH 09/13] Fixed setup.py to use package data and automatically pull hydes version of typogrify --- .gitignore | 1 + README.markdown | 39 ++-- distribute_setup.py | 485 ++++++++++++++++++++++++++++++++++++++++++++ hyde/version.py | 2 +- req-2.6.txt | 2 - req-2.7.txt | 7 - requirements.txt | 9 +- setup.py | 139 ++++++++++++- 8 files changed, 645 insertions(+), 39 deletions(-) create mode 100644 distribute_setup.py delete mode 100644 req-2.6.txt delete mode 100644 req-2.7.txt diff --git a/.gitignore b/.gitignore index 3087f00..1d0aece 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ build .idea PYSMELLTAGS .noseids +*.tar.gz diff --git a/README.markdown b/README.markdown index 5a264c4..b31ca99 100644 --- a/README.markdown +++ b/README.markdown @@ -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 diff --git a/distribute_setup.py b/distribute_setup.py new file mode 100644 index 0000000..048b09b --- /dev/null +++ b/distribute_setup.py @@ -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:]) \ No newline at end of file diff --git a/hyde/version.py b/hyde/version.py index ad54268..a53d5a3 100644 --- a/hyde/version.py +++ b/hyde/version.py @@ -3,4 +3,4 @@ Handles hyde version TODO: Use fabric like versioning scheme """ -__version__ = '0.6.0' +__version__ = '0.7b1' diff --git a/req-2.6.txt b/req-2.6.txt deleted file mode 100644 index b51f34a..0000000 --- a/req-2.6.txt +++ /dev/null @@ -1,2 +0,0 @@ -argparse --r req-2.7.txt \ No newline at end of file diff --git a/req-2.7.txt b/req-2.7.txt deleted file mode 100644 index 641e685..0000000 --- a/req-2.7.txt +++ /dev/null @@ -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 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e783f96..0795680 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,8 @@ --r req-2.6.txt \ No newline at end of file +argparse +commando +PyYAML +Markdown +MarkupSafe +smartypants +-e git://github.com/hyde/typogrify.git#egg=typogrify +Jinja2 \ No newline at end of file diff --git a/setup.py b/setup.py index 29f4edd..c37b337 100644 --- a/setup.py +++ b/setup.py @@ -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, ) From 45b960fb5263d71f19372a212550c2fad98a0357 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Sun, 20 Feb 2011 18:13:42 +0530 Subject: [PATCH 10/13] Added manifest file --- MANIFEST.IN | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 MANIFEST.IN diff --git a/MANIFEST.IN b/MANIFEST.IN new file mode 100644 index 0000000..caf9dd6 --- /dev/null +++ b/MANIFEST.IN @@ -0,0 +1,5 @@ +include setup.py +include distribute_setup.py +include tests/* +exclude *egg* +recursive-include *.py *.html *.xml *.j2 *.css *.less *.yaml *.js *.png *.ico *.txt *.markdown *.md \ No newline at end of file From 4d9b428ace1c6b476cf0d13137e83cd6e829f378 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Sun, 20 Feb 2011 18:57:39 +0530 Subject: [PATCH 11/13] Changing case of MANIFEST.in file --- MANIFEST.IN | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 MANIFEST.IN diff --git a/MANIFEST.IN b/MANIFEST.IN deleted file mode 100644 index caf9dd6..0000000 --- a/MANIFEST.IN +++ /dev/null @@ -1,5 +0,0 @@ -include setup.py -include distribute_setup.py -include tests/* -exclude *egg* -recursive-include *.py *.html *.xml *.j2 *.css *.less *.yaml *.js *.png *.ico *.txt *.markdown *.md \ No newline at end of file From 59a3445c74949764b8bb07a0f4d2ae56e19b641c Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Sun, 20 Feb 2011 18:58:05 +0530 Subject: [PATCH 12/13] Renamed MANIFEST file --- MANIFEST.in | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..b412281 --- /dev/null +++ b/MANIFEST.in @@ -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 *.* From 65e10bab7ed29384237759d4582c97a4bcb83435 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Fri, 4 Mar 2011 13:01:16 +0530 Subject: [PATCH 13/13] Fixed server to refresh changes for listing pages as well --- hyde/server.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hyde/server.py b/hyde/server.py index c22e101..14c874f 100644 --- a/hyde/server.py +++ b/hyde/server.py @@ -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: