Skip to content
This repository was archived by the owner on Aug 27, 2021. It is now read-only.

Adding the template command to ino #273

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ino/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
from ino.commands.upload import Upload
from ino.commands.serial import Serial
from ino.commands.listmodels import ListModels
from ino.commands.template import Template
59 changes: 15 additions & 44 deletions ino/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from ino.commands.base import Command
from ino.exc import Abort
from ino.utils import format_available_options, list_subdirs
from ino.utils import format_available_options, list_subdirs, copytree


class Init(Command):
Expand Down Expand Up @@ -41,49 +41,20 @@ def setup_arg_parser(self, parser):
default=self.default_template)

def run(self, args):
templates_dir = self.e.templates_dir
template = args.template
candidate = os.path.join(templates_dir, template)
if not os.path.isdir(candidate):
w = os.walk(self.e.templates_dir)
found = False
for dirname, dirs, _ in w:
if template in dirs:
found = True
candidate = os.path.join(templates_dir, dirname, template)
break
if not found:
raise Abort("Template does not exist")
try:
copytree(os.path.join(self.e['templates_dir'], args.template),
'.', ignore=lambda *args: ['manifest.ini'])
copytree(candidate, '.', ignore=lambda *args: ['manifest.ini'])
except shutil.Error as e:
raise Abort(str(e))


def copytree(src, dst, symlinks=False, ignore=None):
"""
Tweaked version of shutil.copy tree that allows to copy
to current directory
"""
names = os.listdir(src)
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()

if dst == '.':
if os.listdir(dst):
raise shutil.Error('Current directory is not empty')
else:
os.makedirs(dst)

errors = []
for name in names:
if name in ignored_names:
continue
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
copytree(srcname, dstname, symlinks, ignore)
else:
shutil.copy2(srcname, dstname)
except (IOError, os.error), why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except shutil.Error, err:
errors.extend(err.args[0])
if errors:
raise shutil.Error(errors)
130 changes: 130 additions & 0 deletions ino/commands/template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# -*- coding: utf-8; -*-

import os
import os.path
import shutil

from configobj import ConfigObj

from ino.commands.base import Command
from ino.exc import Abort
from ino.filters import colorize
from ino.utils import format_available_options, list_subdirs, copytree


class Template(Command):
"""
Manage templates in the builtin template directory
"""

name = 'template'
help_line = "Manage templates in the builtin template (aka examples) directory"

def setup_arg_parser(self, parser):
super(Template, self).setup_arg_parser(parser)
parser.add_argument('-u', '--update', action='store_true',
help='Fetch all examples from the associated arduino installation and copy them to the template directory')
parser.add_argument('-r', '--remove', nargs='+',
help='Remove all specified templates below the provided directory level')
parser.add_argument('-a', '--add', nargs='+',
help='Add the provided directory to the templates directory. Every provided directory is expected to have a src/ subdirectory containing .ino files')
parser.add_argument('-l', '--list', action='store_true',
help='List all available templates. This option will deactivate all other options provided with the same command.')
parser.add_argument('-p', '--prefix', nargs=1,
help='Used only in conjunction with the --add option. Specifies the path where to save the template within the templates directory (hierarchy).')

def run(self, args):
templates_dir = self.e.templates_dir
if args.list:
def is_ino(filename):
return filename.endswith('.ino')
def strip_path(path):
path = path.replace(templates_dir,'')
if path.endswith('/src'):
path = path.replace('/src','')
if path.startswith('/'):
path = path[1:]
return path
w = os.walk(templates_dir)
ts = [dirname for dirname,_,fs in w if any(map(is_ino, fs))]
# Sort the array inplace
ts.sort()
ts = map(strip_path, ts)
curr_head = ""
curr_indent = 0
for template in ts:
head,tail = os.path.split(template)
if tail:
if not head:
print colorize(tail, 'yellow')
elif head == curr_head:
print '\t'*curr_indent + colorize(tail, 'yellow')
else:
curr_head = head
curr_indent = 0
for level in head.split('/'):
print '\t'*curr_indent + colorize(level, 'yellow')
curr_indent += 1
print '\t'*curr_indent + colorize(tail, 'yellow')
else:
# It makes no sense to allow the list option with any of the other options, because it is not
# clear which state to print out: before or after the other commands
if args.update:
try:
self.e.find_arduino_dir('arduino_examples_dir', ['examples'],
human_name='Arduino standard examples')
except Abort:
# None of the available directories has an 'examples' subdirectory -> let the user know about it
print colorize("Unable to locate examples subdirectory in any of the search directories", 'red')
else:
# We found an 'examples' subdirectory
arduino_examples = os.walk(self.e.arduino_examples_dir)
# The following statement is a neat little trick to descent the directory structure until we actually
# reach the .ino sketches. The variable 'directory' will be the full path relative to the top level
# examples directory and files will be the sketches. From there on we just have to copy the files into
# the template dir, preserving the example 'categories' (e.g. 01.Basics/Blink)
copy_candidates = [(directory, files) for (directory,_,files) in arduino_examples if files]
for src_dir, files in copy_candidates:
# Reroot the subsrc_dir to our templates src_dir
dst_dir = src_dir.replace(self.e.arduino_examples_dir, templates_dir)
# Check if this template already exists, if so skip copy
if os.path.isdir(dst_dir):
print colorize("Template destination already exists. Nothing will be done.", 'yellow')
else:
dst_dir_lib = os.path.join(dst_dir, 'lib')
dst_dir_src = os.path.join(dst_dir, 'src')
# Create empty lib folder as arduino examples don't have external libraries
os.makedirs(dst_dir_lib)
print colorize("Copying %s to %s"%(src_dir, dst_dir_src), 'green')
copytree(src_dir, dst_dir_src)
if args.remove:
for src_dir in args.remove:
try:
shutil.rmtree(os.path.join(templates_dir, src_dir))
except OSError:
w = os.walk(templates_dir)
found = False
for dirname, dirs, _ in w:
if src_dir in dirs:
found = True
shutil.rmtree(os.path.join(templates_dir, dirname, src_dir))
break
if not found:
print colorize("Template does not exist", 'red')
if args.add:
for src_dir in args.add:
content = os.listdir(src_dir)
if not 'src' in content:
print colorize("Provided directory %s does not contain a src/ subdirectory!"%src_dir, 'red')
else:
name = src_dir.split('/')[-1]
if args.prefix:
prefix = args.prefix[0]
dst_dir = os.path.join(templates_dir, prefix)
else:
dst_dir = os.path.join(templates_dir, name)
print colorize("Copying %s to %s"%(src_dir, dst_dir), 'green')
try:
copytree(src_dir, dst_dir)
except OSError:
print colorize("Template already exists!", 'red')
2 changes: 1 addition & 1 deletion ino/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def main():
args = parser.parse_args()

try:
run_anywhere = "init clean list-models serial"
run_anywhere = "init clean list-models serial template"

in_project_dir = os.path.isdir(e.src_dir)
if not in_project_dir and current_command not in run_anywhere:
Expand Down
42 changes: 42 additions & 0 deletions ino/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8; -*-

import shutil
import os.path
import itertools

Expand Down Expand Up @@ -58,3 +59,44 @@ def format_available_options(items, head_width, head_color='cyan',
val)
for key, val in items]
return '\n'.join(lines)


def copytree(src, dst, symlinks=False, ignore=None):
"""
Tweaked version of shutil.copy tree that allows to copy
to current directory
"""
names = os.listdir(src)
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()

if dst == '.':
if os.listdir(dst):
raise shutil.Error('Current directory is not empty')
else:
os.makedirs(dst)

errors = []
for name in names:
if name in ignored_names:
continue
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
copytree(srcname, dstname, symlinks, ignore)
else:
shutil.copy2(srcname, dstname)
except (IOError, os.error), why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except shutil.Error, err:
errors.extend(err.args[0])
if errors:
raise shutil.Error(errors)