Browse Source

hyde init complete

main
Lakshmi Vyasarajan 14 years ago
parent
commit
e75cefea33
16 changed files with 306 additions and 35 deletions
  1. +1
    -2
      LICENSE
  2. +25
    -23
      hyde/engine.py
  3. +5
    -0
      hyde/exceptions.py
  4. +78
    -5
      hyde/fs.py
  5. +40
    -1
      hyde/layout.py
  6. +0
    -0
      hyde/layouts/basic/README.markdown
  7. +0
    -0
      hyde/layouts/basic/info.yaml
  8. +0
    -0
      hyde/layouts/basic/layout/analytics.html
  9. +0
    -0
      hyde/layouts/basic/layout/base.html
  10. +0
    -0
      hyde/layouts/basic/layout/devmode.html
  11. +0
    -0
      hyde/layouts/basic/layout/root.html
  12. +2
    -2
      hyde/main.py
  13. +0
    -1
      hyde/tests/data/unicode.txt
  14. +71
    -1
      hyde/tests/test_fs.py
  15. +45
    -0
      hyde/tests/test_initialize.py
  16. +39
    -0
      hyde/tests/test_layout.py

+ 1
- 2
LICENSE View File

@@ -18,5 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

THE SOFTWARE.

+ 25
- 23
hyde/engine.py View File

@@ -2,10 +2,15 @@
"""
Implements the hyde entry point commands
"""
import sys
from commando import Application, command, subcommand, param
from version import __version__
from commando import *
from hyde.exceptions import HydeException
from hyde.fs import File, Folder
from hyde.layout import Layout
from hyde.version import __version__

import os

HYDE_LAYOUTS = "HYDE_LAYOUTS"

class Engine(Application):
"""
@@ -14,9 +19,9 @@ class Engine(Application):

@command(description='hyde - a python static website generator',
epilog='Use %(prog)s {command} -h to get help on individual commands')
@param('-v', '--version', action='version', version='%(prog)s ' + __version__)
@param('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
def main(self, params):
@version('-v', '--version', version='%(prog)s ' + __version__)
@store('-s', '--sitepath', default='.', help="Location of the hyde site")
def main(self, args):
"""
Will not be executed. A sub command is required. This function exists to provide
common parameters for the subcommands and some generic stuff like version and
@@ -25,24 +30,21 @@ class Engine(Application):
pass

@subcommand('init', help='Create a new hyde site')
@param('-t', '--template', action='store', default='basic', dest='template',
help='Overwrite the current site if it exists')
@param('-f', '--force', action='store_true', default=False, dest='overwrite',
help='Overwrite the current site if it exists')
def init(self, params):
@store('-l', '--layout', default='basic', help='Layout for the new site')
@true('-f', '--force', default=False, dest='overwrite',
help='Overwrite the current site if it exists')
def init(self, args):
"""
The initialize command. Creates a new site from the template at the given
sitepath.
"""
print params.sitepath
print params.template
print params.overwrite

def start(self):
"""
main()
"""
args = self.parse(sys.argv[1:])
self.run(args)


sitepath = File(args.sitepath)
if sitepath.exists and not args.overwrite:
raise HydeException("The given site path[%s] is not empty" % sitepath)
layout = Layout.find_layout(args.layout)
if not layout.exists:
raise HydeException(
"The given layout is invalid. Please check if you have the `layout` "
"is in the right place and the environment variable has been setup"
"properly")
layout.copy_to(args.sitepath)

+ 5
- 0
hyde/exceptions.py View File

@@ -0,0 +1,5 @@
class HydeException(Exception):
"""
Base class for exceptions from hyde
"""
pass

+ 78
- 5
hyde/fs.py View File

@@ -1,14 +1,15 @@
# -*- 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.
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
# import fnmatch
import os
# import shutil
import shutil
# from datetime import datetime

# pylint: disable-msg=E0611
@@ -28,6 +29,19 @@ class FS(object):
def __repr__(self):
return self.path

def __eq__(self, other):
return str(self) == str(other)

def __ne__(self, other):
return str(self) != str(other)

@property
def exists(self):
"""
Does the file system object exist?
"""
return os.path.exists(self.path)

@property
def name(self):
"""
@@ -42,6 +56,27 @@ class FS(object):
"""
return Folder(os.path.dirname(self.path))


@staticmethod
def file_or_folder(path):
"""
Returns a File or Folder object that would represent the given path.
"""
target = str(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`. `destination` must be
an instance of File or Folder.
"""
if os.path.isdir(str(destination)):
return FS.file_or_folder(Folder(destination).child(self.name))
else:
return destination


class File(FS):
"""
The File object.
@@ -85,6 +120,16 @@ class File(FS):
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.
"""
shutil.copy(self.path, str(destination))
return self.__get_destination__(destination)


class Folder(FS):
"""
Represents a directory.
@@ -102,4 +147,32 @@ class Folder(FS):
"""
Returns a path of a child item represented by `name`.
"""
return os.path.join(self.path, name)
return os.path.join(self.path, name)

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:
os.makedirs(self.path)
except os.error:
pass
return self

def delete(self):
"""
Deletes the directory if it exists.
"""
if self.exists:
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)
shutil.copytree(self.path, str(target))
return target

+ 40
- 1
hyde/layout.py View File

@@ -1 +1,40 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
"""
Classes, functions and utilties related to hyde layouts
"""
import os

from hyde.fs import File, Folder

HYDE_DATA = "HYDE_DATA"
LAYOUTS = "layouts"

class Layout(object):
"""
Represents a layout package
"""

@staticmethod
def find_layout(layout_name="basic"):
"""
Find the layout with a given name.
Search order:
1. env(HYDE_DATA)
2. <hyde script path>/layouts/
"""
layout_folder = None
if HYDE_DATA in os.environ:
layout_folder = Layout._get_layout_folder(os.environ[HYDE_DATA], layout_name)
if not layout_folder:
layout_folder = Layout._get_layout_folder(File(__file__).parent, layout_name)
return layout_folder

@staticmethod
def _get_layout_folder(root, layout_name="basic"):
"""
Finds the layout folder from the given root folder.
If it does not exist, return None
"""
layout_folder = Folder(str(root)).child_folder(LAYOUTS).child_folder(layout_name)
return layout_folder if layout_folder.exists else None


data/layouts/basic/README.markdown → hyde/layouts/basic/README.markdown View File


data/layouts/basic/info.yaml → hyde/layouts/basic/info.yaml View File


data/layouts/basic/layout/analytics.html → hyde/layouts/basic/layout/analytics.html View File


data/layouts/basic/layout/base.html → hyde/layouts/basic/layout/base.html View File


data/layouts/basic/layout/devmode.html → hyde/layouts/basic/layout/devmode.html View File


data/layouts/basic/layout/root.html → hyde/layouts/basic/layout/root.html View File


hyde/hyde.py → hyde/main.py View File

@@ -3,7 +3,7 @@
"""
The hyde executable
"""
from engine import Engine
from hyde.engine import Engine

if __name__ == "__main__":
Engine().start()
Engine().run()

+ 0
- 1
hyde/tests/data/unicode.txt View File

@@ -1 +0,0 @@
åßcdeƒ

+ 71
- 1
hyde/tests/test_fs.py View File

@@ -8,7 +8,9 @@ Use nose
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__)
@@ -49,9 +51,76 @@ def test_child_folder():
assert hasattr(c, 'child_folder')
assert str(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(str(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_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'))

@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_read_all():
utxt = u'åßcdeƒ'
path = DATA_ROOT.child('unicode.txt')
@@ -60,7 +129,8 @@ def test_read_all():

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')


+ 45
- 0
hyde/tests/test_initialize.py View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
"""
Use nose
`$ pip install nose`
`$ nosetests`
"""


from hyde.engine import Engine
from hyde.exceptions import HydeException
from hyde.fs import FS, File, Folder

from nose.tools import raises, with_setup, nottest

TEST_SITE = File(__file__).parent.child_folder('_test')

@nottest
def create_test_site():
TEST_SITE.make()

@nottest
def delete_test_site():
TEST_SITE.delete()

@raises(HydeException)
@with_setup(create_test_site, delete_test_site)
def test_ensure_exception_when_sitepath_exists():
e = Engine()
e.run(e.parse(['-s', str(TEST_SITE), 'init']))

@with_setup(create_test_site, delete_test_site)
def test_ensure_no_exception_when_sitepath_exists_when_forced():
e = Engine()
e.run(e.parse(['-s', str(TEST_SITE), 'init', '-f']))
assert True #No Exception

@with_setup(create_test_site, delete_test_site)
def test_ensure_no_exception_when_sitepath_does_not_exist():
e = Engine()
TEST_SITE.delete()
e.run(e.parse(['-s', str(TEST_SITE), 'init', '-f']))
assert TEST_SITE.exists
assert TEST_SITE.child_folder('layout').exists
assert File(TEST_SITE.child('info.yaml')).exists


+ 39
- 0
hyde/tests/test_layout.py View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
"""
Use nose
`$ pip install nose`
`$ nosetests`
"""

from hyde.fs import File, Folder
from hyde.layout import Layout, HYDE_DATA, LAYOUTS
from nose.tools import raises, with_setup, nottest
import os

DATA_ROOT = File(__file__).parent.child_folder('data')
LAYOUT_ROOT = DATA_ROOT.child_folder(LAYOUTS)

@nottest
def setup_data():
DATA_ROOT.make()

@nottest
def cleanup_data():
DATA_ROOT.delete()

def test_find_layout_from_package_dir():
f = Layout.find_layout()
assert f.name == 'basic'
assert f.child_folder('layout').exists

@with_setup(setup_data, cleanup_data)
def test_find_layout_from_env_var():
f = Layout.find_layout()
LAYOUT_ROOT.make()
f.copy_to(LAYOUT_ROOT)
os.environ[HYDE_DATA] = str(DATA_ROOT)
f = Layout.find_layout()
assert f.parent == LAYOUT_ROOT
assert f.name == 'basic'
assert f.child_folder('layout').exists
del os.environ[HYDE_DATA]

Loading…
Cancel
Save