Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doc cleanup #5

Merged
6 commits merged into from Feb 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 25 additions & 4 deletions voodoo/helpers.py
Expand Up @@ -11,7 +11,8 @@
from ._compat import to_unicode


def formatm(action, msg='', color='', on_color='', bright=True, indent=12):
def format_message(action, msg='', color='', on_color='', bright=True, indent=12):
"""Format message."""
action = action.rjust(indent, ' ')
color = getattr(Fore, color.upper(), '')
on_color = getattr(Back, on_color.upper(), '')
Expand All @@ -26,11 +27,16 @@ def formatm(action, msg='', color='', on_color='', bright=True, indent=12):
])


def pformat(*args, **kwargs):
print(formatm(*args, **kwargs))
def print_format(*args, **kwargs):
"""Like format_message but prints it."""
print(format_message(*args, **kwargs))


def make_dirs(*lpath):
"""Ensure the directories exist.

lpath: list of directories
"""
path = os.path.join(*lpath)
try:
os.makedirs(os.path.dirname(path))
Expand All @@ -41,30 +47,45 @@ def make_dirs(*lpath):


def read_file(path, encoding='utf8'):
"""Open file and return the content. By default the file is assumed to be
encoded in UTF-8.
"""
with io.open(path, 'rt', encoding=encoding) as f:
content = f.read()
return content


def file_has_this_content(path, content, encoding='utf8'):
"""True if the file is identical to the content.

When comparing two files it is best to use the files_are_identical
function.
"""
with io.open(path, 'rt', encoding=encoding) as f:
indeed = content == f.read()
return indeed


def files_are_identical(path_1, path_2):
"""True if files are identical, False otherwise."""
return filecmp.cmp(path_1, path_2, shallow=False)


def unormalize(text, form='NFD'):
def normalize(text, form='NFD'):
"""Normalize unicode text. Uses the NFD algorithm by default."""
return unicodedata.normalize(form, text)


def create_file(path, content, encoding='utf8'):
"""Create a file at path the content. Content is assumed to be a utf-8 encoded
string.
"""
content = to_unicode(content, encoding)
with io.open(path, 'wt', encoding=encoding) as f:
f.write(content)


def copy_file(path_in, path_out):
shutil.copy2(path_in, path_out)

copy_file.__doc__ = shutil.copy2.__doc__
34 changes: 15 additions & 19 deletions voodoo/main.py
Expand Up @@ -3,10 +3,7 @@

from fnmatch import fnmatch
from functools import reduce
try:
from collections import OrderedDict
except ImportError:
from .ordereddict import OrderedDict
from collections import OrderedDict
import datetime
import json
import os
Expand All @@ -19,7 +16,7 @@
from .cli import prompt_bool, prompt
from .vcs import get_vcs_from_url, clone
from .helpers import (
pformat, make_dirs, create_file, copy_file, unormalize, read_file,
print_format, make_dirs, create_file, copy_file, normalize, read_file,
file_has_this_content, files_are_identical)


Expand All @@ -45,6 +42,9 @@
COLOR_IGNORE = 'cyan'
COLOR_DANGER = 'red'

RX_PARAM_PATH = re.compile(r'\[\[\s*(\w+)\s*\]\]', flags=re.IGNORECASE)
RX_PARAM_PATH_COOKIECUTTER = re.compile(r'\{\{\s*(\w+)\s*\}\}', flags=re.IGNORECASE)


def render_skeleton(
src_path, dst_path, data=None, filter_this=None, include_this=None,
Expand Down Expand Up @@ -115,7 +115,7 @@ def get_user_data(src_path, force, quiet):
def_user_data = json.loads(json_src, object_pairs_hook=OrderedDict)
except ValueError as e:
if not quiet:
pformat('Invalid `voodoo.json`', color=COLOR_WARNING)
print_format('Invalid `voodoo.json`', color=COLOR_WARNING)
print(e)
def_user_data = {}

Expand Down Expand Up @@ -194,13 +194,13 @@ def get_name_filter(filter_this, include_this):
decomposed unicode string. In those systems, u'ñ' is read as `\u0303`
instead of `\xf1`.
"""
filter_this = [unormalize(to_unicode(f)) for f in
filter_this = [normalize(to_unicode(f)) for f in
filter_this or DEFAULT_FILTER]
include_this = [unormalize(to_unicode(f)) for f in
include_this = [normalize(to_unicode(f)) for f in
include_this or DEFAULT_INCLUDE]

def fullmatch(path, pattern):
path = unormalize(path)
path = normalize(path)
name = os.path.basename(path)
return fnmatch(name, pattern) or fnmatch(path, pattern)

Expand All @@ -218,17 +218,13 @@ def must_filter(path):
return must_filter


rx_param_path = re.compile(r'\[\[\s*(\w+)\s*\]\]', flags=re.IGNORECASE)
rx_param_path_cookiecutter = re.compile(r'\{\{\s*(\w+)\s*\}\}', flags=re.IGNORECASE)


def parametrize_path(path, data):
"""Replace the {{varname}} slots in the path with its real values.
"""
def get_data_value(match):
return data.get(match.group(1), match.group(0))
path = rx_param_path.sub(get_data_value, path)
path = rx_param_path_cookiecutter.sub(get_data_value, path)
path = RX_PARAM_PATH.sub(get_data_value, path)
path = RX_PARAM_PATH_COOKIECUTTER.sub(get_data_value, path)
return path


Expand All @@ -246,7 +242,7 @@ def render_file(dst_path, rel_folder, folder, src_name, dst_name, render_tmpl,

if not os.path.exists(final_path):
if not quiet:
pformat('create', created_path, color=COLOR_OK)
print_format('create', created_path, color=COLOR_OK)
if not pretend:
make_file(src_name, render_tmpl, fullpath, final_path)
return
Expand All @@ -263,12 +259,12 @@ def render_file(dst_path, rel_folder, folder, src_name, dst_name, render_tmpl,
# The existing file is identical.
if identical:
if not quiet:
pformat('identical', created_path, color=COLOR_IGNORE, bright=None)
print_format('identical', created_path, color=COLOR_IGNORE, bright=None)
return

# The existing file is different.
if not quiet:
pformat('conflict', created_path, color=COLOR_DANGER)
print_format('conflict', created_path, color=COLOR_DANGER)
if force:
overwrite = True
elif skip:
Expand All @@ -278,7 +274,7 @@ def render_file(dst_path, rel_folder, folder, src_name, dst_name, render_tmpl,
overwrite = prompt_bool(msg, default=True)

if not quiet:
pformat('force' if overwrite else 'skip', created_path, color=COLOR_WARNING)
print_format('force' if overwrite else 'skip', created_path, color=COLOR_WARNING)

if overwrite and not pretend:
if content is None:
Expand Down
127 changes: 0 additions & 127 deletions voodoo/ordereddict.py

This file was deleted.

8 changes: 6 additions & 2 deletions voodoo/script.py
Expand Up @@ -14,6 +14,9 @@


def new_project(path, tmpl=None, **options):
"""Creates a new project using tmpl at path."""
if tmpl is None:
raise ValueError("tmpl must be be a path to the template.")
data = default_context.copy()
render_skeleton(
tmpl, path, data=data,
Expand All @@ -31,6 +34,7 @@ def error(self, message):


def run():
"""Voodoo's CLI entry point."""
parser = DefaultHelpParser(description='Create a project from a Voodoo project template.')
parser.add_argument('tmpl',
help='Fullpath or a git/hg URL of a project template ')
Expand All @@ -46,8 +50,8 @@ def run():
help='Suppress status output')

args = parser.parse_args()
da = vars(args)
new_project(da.pop('path'), **da)
dict_args = vars(args)
new_project(dict_args.pop('path'), **dict_args)


if __name__ == '__main__':
Expand Down
6 changes: 3 additions & 3 deletions voodoo/vcs.py
Expand Up @@ -10,13 +10,12 @@

rx_vcs = re.compile(r'^(git|hg)(@|\+[a-z]+://|://)', re.IGNORECASE)


VCS = namedtuple('VCS', 'type url')


def get_vcs_from_url(url):
"""Try to identify the URL as a git or mercurial repo and return a
namedtuple `(type url)` if have success.
"""Try to identify the URL as a git or mercurial repo and return a namedtuple
`(type url)` if have success.
"""
match = rx_vcs.match(url)
if match:
Expand All @@ -31,6 +30,7 @@ def get_vcs_from_url(url):


def normalize_url(url):
"""Ensure the url doesn't start with either git or hg."""
if not url.startswith(('git+', 'hg+')):
return url
return url[4:]
Expand Down