Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from michaeljoseph/cli
Initial CLI to capture and apply mould replacements
- Loading branch information
Showing
10 changed files
with
308 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
from .cli import main | ||
|
||
import mould.cli | ||
mould.cli.main() | ||
main(prog_name='mould') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,88 @@ | ||
import logging | ||
import json | ||
|
||
import click | ||
|
||
log = logging.getLogger(__name__) | ||
from . import mould | ||
from . import read_directory | ||
from .transform import preview | ||
|
||
|
||
@click.command() | ||
@click.option('--count', default=1, help='Number of greetings.') | ||
@click.option('--name', prompt='Your name', help='The person to greet.') | ||
@click.option('--debug', default=False, help='Debug mode.') | ||
def main(count, name, debug): | ||
"""Simple program that greets NAME for a total of COUNT times.""" | ||
logging.basicConfig(level=logging.DEBUG if debug else logging.INFO) | ||
@click.argument('source', type=click.Path(exists=True)) | ||
@click.argument('destination', type=click.Path()) | ||
@click.option('--debug', is_flag=True, default=False, help='Debug mode.') | ||
@click.option('--dry-run', is_flag=True, default=False, help='Dry run.') | ||
def main(source, destination, debug, dry_run): | ||
|
||
for x in range(count): | ||
click.echo('Hello %s!' % name) | ||
click.secho( | ||
'Reading from {} and writing to {}'.format(source, destination), | ||
fg='green', | ||
) | ||
|
||
log.debug('Goodbye %s!' % name) | ||
source_files = read_directory(source) | ||
|
||
replacements = {} | ||
|
||
done = False | ||
while not done: | ||
try: | ||
pattern = click.prompt( | ||
click.style( | ||
'Enter a pattern to search for ' | ||
'(enter when done)', | ||
fg='green' | ||
), | ||
default='', | ||
show_default=False | ||
) | ||
if pattern == '': | ||
done = True | ||
continue | ||
|
||
click.echo( | ||
preview( | ||
source_files, | ||
{ | ||
pattern: '<temporary-name>' | ||
}, | ||
) | ||
) | ||
|
||
name = click.prompt( | ||
click.style( | ||
'Give this pattern a name (or enter to discard)', | ||
fg='green' | ||
), | ||
default='', | ||
show_default=False | ||
) | ||
|
||
if name: | ||
replacements[pattern] = name | ||
|
||
if replacements: | ||
click.secho( | ||
'Current replacements {}'.format(json.dumps(replacements)), | ||
fg='yellow' | ||
) | ||
except click.Abort: | ||
done = True | ||
|
||
if replacements: | ||
if not click.confirm(click.style( | ||
'Confirm moulding {} to {}, with replacements:\n{}\n'.format( | ||
source, | ||
destination, | ||
# FIXME: print replacements better | ||
json.dumps(replacements)), | ||
fg='yellow' | ||
)): | ||
import sys | ||
sys.exit(0) | ||
|
||
if not dry_run: | ||
mould( | ||
source, | ||
replacements, | ||
destination, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
def read_ignore(ignore_content): | ||
return [ | ||
ignore_line | ||
for ignore_line in ignore_content.split() | ||
if not ignore_line.startswith('#') | ||
] | ||
|
||
|
||
def remove_ignores(file_paths, ignore_list): | ||
""" | ||
Remove files that match gitignore patterns | ||
:param file_paths: | ||
:param ignore_list: | ||
:return: | ||
""" | ||
# https://stackoverflow.com/a/25230908/5549 | ||
from fnmatch import fnmatch | ||
matches = [] | ||
for ignore in ignore_list: | ||
file_paths = [ | ||
n for n | ||
in file_paths | ||
if n.startswith('#') or not fnmatch(n, ignore) | ||
] | ||
matches.extend(file_paths) | ||
return matches |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,95 @@ | ||
import difflib | ||
|
||
import click | ||
|
||
def replace_directory_entries(directory_entries, replacements): | ||
""" | ||
Perform `replacements` substitutions on `DirectoryEntry` instances. | ||
def replace_directory_entries(directory_entries, replacements, preview=False): | ||
Also mention the cookiecutter thing? | ||
https://docs.python.org/3.3/library/stdtypes.html#str.replace | ||
Specifically the path elements | ||
:param directory_entries: | ||
:param replacements: A dict where the key is the [value of the needle] | ||
and the value | ||
:return: | ||
""" | ||
replaced_entries = [] | ||
|
||
for directory_entry in directory_entries: | ||
entry = dict(directory_entry) | ||
# make a copy of the current entry | ||
directory = dict(directory_entry) | ||
|
||
for search, replace in replacements.items(): | ||
# transform the value into a cookiecutter variable | ||
replace = '{{cookiecutter.' + replace + '}}' | ||
entry['path'] = entry['path'].replace(search, replace) | ||
|
||
for file_record in entry['files']: | ||
old_path = file_record['path'] | ||
# replace directory path names | ||
directory['path'] = directory['path'].replace(search, replace) | ||
|
||
for file_record in directory['files']: | ||
# transform the file path | ||
file_record['path'] = file_record['path'].replace( | ||
search, | ||
replace | ||
) | ||
|
||
if not file_record['binary']: | ||
before = file_record['content'] | ||
# don't try to run replace on binary files | ||
is_text_file = not file_record['binary'] | ||
if is_text_file: | ||
# transform the file content | ||
file_record['content'] = file_record['content'].replace( | ||
search, | ||
replace | ||
) | ||
if preview: | ||
click.echo_via_pager('\n'.join( | ||
difflib.unified_diff( | ||
before.splitlines(), | ||
file_record['content'].splitlines(), | ||
fromfile=old_path, | ||
tofile=file_record['path'], | ||
lineterm='', | ||
) | ||
)) | ||
|
||
replaced_entries.append(entry) | ||
|
||
replaced_entries.append(directory) | ||
|
||
return replaced_entries | ||
|
||
|
||
def preview(directory_entries, replacements): | ||
preview_content = [] | ||
|
||
for directory_entry in directory_entries: | ||
# make a copy of the current entry | ||
directory = dict(directory_entry) | ||
|
||
for search, replace in replacements.items(): | ||
# transform the value into a cookiecutter variable | ||
replace = '{{cookiecutter.' + replace + '}}' | ||
|
||
# replace directory path names | ||
# directory['path'] = directory['path'].replace(search, replace) | ||
|
||
for file_record in directory['files']: | ||
# save current path for diff | ||
old_path = file_record['path'] | ||
|
||
# transform the file path | ||
new_path = old_path.replace( | ||
search, | ||
replace | ||
) | ||
|
||
# don't try to run replace on binary files | ||
is_text_file = not file_record['binary'] | ||
if is_text_file: | ||
# save current content for diff | ||
old_content = file_record['content'] | ||
|
||
# transform the file content | ||
new_content = old_content.replace( | ||
search, | ||
replace | ||
) | ||
|
||
preview_content.append('\n'.join(difflib.unified_diff( | ||
old_content.splitlines(), | ||
new_content.splitlines(), | ||
fromfile=old_path, | ||
tofile=new_path, | ||
lineterm='', | ||
))) | ||
|
||
return '\n'.join(preview_content) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.