gearbox 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gearbox/__init__.py +0 -0
- gearbox/command.py +46 -0
- gearbox/commandmanager.py +82 -0
- gearbox/commands/__init__.py +0 -0
- gearbox/commands/basic_package/__init__.py +3 -0
- gearbox/commands/basic_package/command.py +99 -0
- gearbox/commands/basic_package/template/+package+/__init__.py +1 -0
- gearbox/commands/basic_package/template/MANIFEST.in_tmpl +2 -0
- gearbox/commands/basic_package/template/README.rst_tmpl +17 -0
- gearbox/commands/basic_package/template/setup.cfg_tmpl +0 -0
- gearbox/commands/basic_package/template/setup.py_tmpl +31 -0
- gearbox/commands/help.py +85 -0
- gearbox/commands/patch.py +160 -0
- gearbox/commands/scaffold.py +168 -0
- gearbox/commands/serve.py +893 -0
- gearbox/commands/setup_app.py +142 -0
- gearbox/main.py +258 -0
- gearbox/template.py +35 -0
- gearbox/utils/__init__.py +0 -0
- gearbox/utils/copydir.py +304 -0
- gearbox/utils/log.py +36 -0
- gearbox/utils/plugins.py +18 -0
- gearbox-0.3.0.dist-info/AUTHORS +11 -0
- gearbox-0.3.0.dist-info/LICENSE +7 -0
- gearbox-0.3.0.dist-info/METADATA +266 -0
- gearbox-0.3.0.dist-info/RECORD +29 -0
- gearbox-0.3.0.dist-info/WHEEL +5 -0
- gearbox-0.3.0.dist-info/entry_points.txt +16 -0
- gearbox-0.3.0.dist-info/top_level.txt +1 -0
gearbox/__init__.py
ADDED
|
File without changes
|
gearbox/command.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import inspect
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from .template import GearBoxTemplate
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Command(object):
|
|
10
|
+
deprecated = False
|
|
11
|
+
|
|
12
|
+
def __init__(self, app, app_args, cmd_name=None):
|
|
13
|
+
self.app = app
|
|
14
|
+
self.app_args = app_args
|
|
15
|
+
self.cmd_name = cmd_name
|
|
16
|
+
|
|
17
|
+
def get_description(self):
|
|
18
|
+
"""Override to provide custom description for command."""
|
|
19
|
+
return inspect.getdoc(self.__class__) or ""
|
|
20
|
+
|
|
21
|
+
def get_parser(self, prog_name):
|
|
22
|
+
"""Override to add command options."""
|
|
23
|
+
parser = argparse.ArgumentParser(
|
|
24
|
+
description=self.get_description(), prog=prog_name, add_help=False
|
|
25
|
+
)
|
|
26
|
+
return parser
|
|
27
|
+
|
|
28
|
+
def take_action(self, parsed_args):
|
|
29
|
+
"""Override to do something useful."""
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
def run(self, parsed_args):
|
|
33
|
+
self.take_action(parsed_args)
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TemplateCommand(Command):
|
|
38
|
+
template = GearBoxTemplate()
|
|
39
|
+
|
|
40
|
+
def get_template_path(self):
|
|
41
|
+
module = sys.modules[self.__class__.__module__]
|
|
42
|
+
module_path = module.__file__
|
|
43
|
+
return os.path.join(os.path.abspath(os.path.dirname(module_path)), "template")
|
|
44
|
+
|
|
45
|
+
def run_template(self, output_dir, opts):
|
|
46
|
+
self.template.run(self.get_template_path(), output_dir, vars(opts))
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Discover and lookup command plugins.
|
|
3
|
+
|
|
4
|
+
This comes from OpenStack cliff.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib.metadata
|
|
8
|
+
import logging
|
|
9
|
+
|
|
10
|
+
LOG = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EntryPointWrapper(object):
|
|
14
|
+
"""Wrap up a command class already imported to make it look like a plugin."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, name, command_class):
|
|
17
|
+
self.name = name
|
|
18
|
+
self.command_class = command_class
|
|
19
|
+
|
|
20
|
+
def load(self, require=False):
|
|
21
|
+
return self.command_class
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CommandManager(object):
|
|
25
|
+
"""Discovers commands and handles lookup based on argv data.
|
|
26
|
+
|
|
27
|
+
:param namespace: String containing the setuptools entrypoint namespace
|
|
28
|
+
for the plugins to be loaded. For example,
|
|
29
|
+
``'cliff.formatter.list'``.
|
|
30
|
+
:param convert_underscores: Whether cliff should convert underscores to
|
|
31
|
+
spaces in entry_point commands.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, namespace, convert_underscores=True):
|
|
35
|
+
self.commands = {}
|
|
36
|
+
self.namespace = namespace
|
|
37
|
+
self.convert_underscores = convert_underscores
|
|
38
|
+
self._load_commands()
|
|
39
|
+
|
|
40
|
+
def _load_commands(self):
|
|
41
|
+
# NOTE(jamielennox): kept for compatability.
|
|
42
|
+
self.load_commands(self.namespace)
|
|
43
|
+
|
|
44
|
+
def load_commands(self, namespace):
|
|
45
|
+
"""Load all the commands from an entrypoint"""
|
|
46
|
+
entry_points = importlib.metadata.entry_points()
|
|
47
|
+
if hasattr(entry_points, "select"):
|
|
48
|
+
entry_points = entry_points.select(group=namespace)
|
|
49
|
+
else:
|
|
50
|
+
entry_points = entry_points.get(namespace, [])
|
|
51
|
+
for ep in entry_points:
|
|
52
|
+
LOG.debug("found command %r", ep.name)
|
|
53
|
+
cmd_name = (
|
|
54
|
+
ep.name.replace("_", " ") if self.convert_underscores else ep.name
|
|
55
|
+
)
|
|
56
|
+
self.commands[cmd_name] = ep
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
def __iter__(self):
|
|
60
|
+
return iter(self.commands.items())
|
|
61
|
+
|
|
62
|
+
def add_command(self, name, command_class):
|
|
63
|
+
self.commands[name] = EntryPointWrapper(name, command_class)
|
|
64
|
+
|
|
65
|
+
def find_command(self, argv):
|
|
66
|
+
"""Given an argument list, find a command and
|
|
67
|
+
return the processor and any remaining arguments.
|
|
68
|
+
"""
|
|
69
|
+
search_args = argv[:]
|
|
70
|
+
name = ""
|
|
71
|
+
while search_args:
|
|
72
|
+
if search_args[0].startswith("-"):
|
|
73
|
+
name = "%s %s" % (name, search_args[0])
|
|
74
|
+
raise ValueError("Invalid command %r" % name)
|
|
75
|
+
next_val = search_args.pop(0)
|
|
76
|
+
name = "%s %s" % (name, next_val) if name else next_val
|
|
77
|
+
if name in self.commands:
|
|
78
|
+
cmd_ep = self.commands[name]
|
|
79
|
+
cmd_factory = cmd_ep.load()
|
|
80
|
+
return (cmd_factory, name, search_args)
|
|
81
|
+
else:
|
|
82
|
+
raise ValueError("Unknown command %r" % next(iter(argv), ""))
|
|
File without changes
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from gearbox.command import TemplateCommand
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MakePackageCommand(TemplateCommand):
|
|
9
|
+
CLEAN_PACKAGE_NAME_RE = re.compile("[^a-zA-Z0-9_]")
|
|
10
|
+
|
|
11
|
+
def get_description(self):
|
|
12
|
+
return "Creates a basic python package"
|
|
13
|
+
|
|
14
|
+
def get_parser(self, prog_name):
|
|
15
|
+
parser = super(MakePackageCommand, self).get_parser(prog_name)
|
|
16
|
+
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"-n",
|
|
19
|
+
"--name",
|
|
20
|
+
dest="project",
|
|
21
|
+
metavar="NAME",
|
|
22
|
+
required=True,
|
|
23
|
+
help="Project Name",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"-o",
|
|
28
|
+
"--output-dir",
|
|
29
|
+
dest="output_dir",
|
|
30
|
+
metavar="OUTPUT_DIR",
|
|
31
|
+
help="Destination directory (by default the project name)",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"-p",
|
|
36
|
+
"--package",
|
|
37
|
+
dest="package",
|
|
38
|
+
metavar="PACKAGE",
|
|
39
|
+
help="Python Package Name",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"-a",
|
|
44
|
+
"--author",
|
|
45
|
+
dest="author",
|
|
46
|
+
metavar="AUTHOR",
|
|
47
|
+
default="Unknown",
|
|
48
|
+
help="Name of the package author",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"-e",
|
|
53
|
+
"--email",
|
|
54
|
+
dest="author_email",
|
|
55
|
+
metavar="AUTHOR_EMAIL",
|
|
56
|
+
help="Email of the package author",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"-u", "--url", dest="url", metavar="URL", help="Project homepage"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"-l",
|
|
65
|
+
"--license",
|
|
66
|
+
dest="license_name",
|
|
67
|
+
metavar="LICENSE_NAME",
|
|
68
|
+
help="License used for the project",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"-d",
|
|
73
|
+
"--description",
|
|
74
|
+
dest="description",
|
|
75
|
+
metavar="DESCRIPTION",
|
|
76
|
+
help="Package description",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
parser.add_argument(
|
|
80
|
+
"-k",
|
|
81
|
+
"--keywords",
|
|
82
|
+
dest="keywords",
|
|
83
|
+
metavar="KEYWORDS",
|
|
84
|
+
help="Package keywords",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return parser
|
|
88
|
+
|
|
89
|
+
def take_action(self, opts):
|
|
90
|
+
if opts.package is None:
|
|
91
|
+
opts.package = self.CLEAN_PACKAGE_NAME_RE.sub("", opts.project.lower())
|
|
92
|
+
|
|
93
|
+
if opts.output_dir is None:
|
|
94
|
+
opts.output_dir = opts.project
|
|
95
|
+
|
|
96
|
+
opts.zip_safe = False
|
|
97
|
+
opts.version = "0.0.1"
|
|
98
|
+
|
|
99
|
+
self.run_template(opts.output_dir, opts)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
About {{package}}
|
|
2
|
+
-------------------------
|
|
3
|
+
|
|
4
|
+
{{package}} is a Python package created using the gearbox makepackage command.
|
|
5
|
+
|
|
6
|
+
Installing
|
|
7
|
+
-------------------------------
|
|
8
|
+
|
|
9
|
+
{{package}} can be installed from pypi::
|
|
10
|
+
|
|
11
|
+
easy_install {{package}}
|
|
12
|
+
|
|
13
|
+
or::
|
|
14
|
+
|
|
15
|
+
pip install {{package}}
|
|
16
|
+
|
|
17
|
+
should just work for most of the users
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
import sys, os
|
|
3
|
+
|
|
4
|
+
here = os.path.abspath(os.path.dirname(__file__))
|
|
5
|
+
try:
|
|
6
|
+
README = open(os.path.join(here, 'README.rst')).read()
|
|
7
|
+
except IOError:
|
|
8
|
+
README = ''
|
|
9
|
+
|
|
10
|
+
version = "{{version}}"
|
|
11
|
+
|
|
12
|
+
setup(name={{repr(project)}},
|
|
13
|
+
version=version,
|
|
14
|
+
description="{{description or ''}}",
|
|
15
|
+
long_description=README,
|
|
16
|
+
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
|
17
|
+
keywords={{repr(keywords or '')}},
|
|
18
|
+
author={{repr(author or '')}},
|
|
19
|
+
author_email={{repr(author_email or '')}},
|
|
20
|
+
url={{repr(url or '')}},
|
|
21
|
+
license={{repr(license_name or '')}},
|
|
22
|
+
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
|
|
23
|
+
include_package_data=True,
|
|
24
|
+
zip_safe=False,
|
|
25
|
+
install_requires=[
|
|
26
|
+
# -*- Extra requirements: -*-
|
|
27
|
+
],
|
|
28
|
+
entry_points="""
|
|
29
|
+
# -*- Entry points: -*-
|
|
30
|
+
""",
|
|
31
|
+
)
|
gearbox/commands/help.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
import traceback
|
|
6
|
+
|
|
7
|
+
from ..command import Command
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HelpAction(argparse.Action):
|
|
11
|
+
"""Provide a custom action so the -h and --help options
|
|
12
|
+
to the main app will print a list of the commands.
|
|
13
|
+
The commands are determined by checking the CommandManager
|
|
14
|
+
instance, passed in as the "default" value for the action.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
|
18
|
+
app = self.default
|
|
19
|
+
parser.print_help(sys.stdout)
|
|
20
|
+
print("\nCommands:")
|
|
21
|
+
command_manager = app.command_manager
|
|
22
|
+
for name, ep in sorted(command_manager):
|
|
23
|
+
try:
|
|
24
|
+
factory = ep.load()
|
|
25
|
+
except Exception:
|
|
26
|
+
print("Could not load %r" % ep)
|
|
27
|
+
if namespace.debug:
|
|
28
|
+
traceback.print_exc(file=sys.stdout)
|
|
29
|
+
continue
|
|
30
|
+
try:
|
|
31
|
+
cmd = factory(app, None)
|
|
32
|
+
if cmd.deprecated:
|
|
33
|
+
continue
|
|
34
|
+
except Exception as err:
|
|
35
|
+
print("Could not instantiate %r: %s\n" % (ep, err))
|
|
36
|
+
if namespace.debug:
|
|
37
|
+
traceback.print_exc(file=sys.stdout)
|
|
38
|
+
continue
|
|
39
|
+
one_liner = cmd.get_description().split("\n")[0]
|
|
40
|
+
print(" %-13s %s" % (name, one_liner))
|
|
41
|
+
sys.exit(0)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class HelpCommand(Command):
|
|
45
|
+
"""print detailed help for another command"""
|
|
46
|
+
|
|
47
|
+
def get_parser(self, prog_name):
|
|
48
|
+
parser = super(HelpCommand, self).get_parser(prog_name)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"cmd",
|
|
51
|
+
nargs="*",
|
|
52
|
+
help="name of the command",
|
|
53
|
+
)
|
|
54
|
+
return parser
|
|
55
|
+
|
|
56
|
+
def take_action(self, parsed_args):
|
|
57
|
+
if not parsed_args.cmd:
|
|
58
|
+
action = HelpAction(None, None, default=self.app)
|
|
59
|
+
action(self.app.parser, self.app.parser, None, None)
|
|
60
|
+
return 1
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
the_cmd = self.app.command_manager.find_command(
|
|
64
|
+
parsed_args.cmd,
|
|
65
|
+
)
|
|
66
|
+
cmd_factory, cmd_name, search_args = the_cmd
|
|
67
|
+
except ValueError:
|
|
68
|
+
# Did not find an exact match
|
|
69
|
+
cmd = parsed_args.cmd[0]
|
|
70
|
+
fuzzy_matches = [
|
|
71
|
+
k[0] for k in self.app.command_manager if k[0].startswith(cmd)
|
|
72
|
+
]
|
|
73
|
+
if not fuzzy_matches:
|
|
74
|
+
raise
|
|
75
|
+
print('Command "%s" matches:' % cmd)
|
|
76
|
+
for fm in sorted(fuzzy_matches):
|
|
77
|
+
print(" %s" % fm)
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
self.app_args.cmd = search_args
|
|
81
|
+
cmd = cmd_factory(self.app, self.app_args)
|
|
82
|
+
full_name = " ".join([self.app.NAME, cmd_name])
|
|
83
|
+
cmd_parser = cmd.get_parser(full_name)
|
|
84
|
+
cmd_parser.print_help(sys.stdout)
|
|
85
|
+
return 0
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
|
|
3
|
+
import fnmatch
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
from argparse import RawDescriptionHelpFormatter
|
|
7
|
+
|
|
8
|
+
from gearbox.command import Command
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PatchCommand(Command):
|
|
12
|
+
def get_description(self):
|
|
13
|
+
return r"""Patches files by replacing, appending or deleting text.
|
|
14
|
+
|
|
15
|
+
This is meant to provide a quick and easy way to replace text and
|
|
16
|
+
code in your projects.
|
|
17
|
+
|
|
18
|
+
Here are a few examples, this will replace all xi:include occurrences
|
|
19
|
+
with py:extends in all the template files recursively:
|
|
20
|
+
|
|
21
|
+
$ gearbox patch -R '*.html' xi:include -r py:extends
|
|
22
|
+
|
|
23
|
+
It is also possible to rely on regex and python for more complex
|
|
24
|
+
replacements, like updating the Copyright year in your documentation:
|
|
25
|
+
|
|
26
|
+
$ gearbox patch -R '*.rst' -x 'Copyright(\s*)(\d+)' -e -r '"Copyright\\g<1>"+__import__("datetime").datetime.utcnow().strftime("%Y")'
|
|
27
|
+
|
|
28
|
+
Works on a line by line basis, so it is not possible to match text
|
|
29
|
+
across multiple lines.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def get_parser(self, prog_name):
|
|
33
|
+
parser = super(PatchCommand, self).get_parser(prog_name)
|
|
34
|
+
parser.formatter_class = RawDescriptionHelpFormatter
|
|
35
|
+
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"pattern", help="The glob pattern of files that should be matched"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"text", help="text that should be looked up in matched files."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"-r",
|
|
46
|
+
"--replace",
|
|
47
|
+
dest="replacement",
|
|
48
|
+
help="Replace occurrences of text with REPLACEMENT",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"-a",
|
|
53
|
+
"--append",
|
|
54
|
+
dest="addition",
|
|
55
|
+
help="Append ADDITION after the line with matching text.",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"-d", "--delete", action="store_true", help="Delete lines matching text."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"-x",
|
|
64
|
+
"--regex",
|
|
65
|
+
dest="regex",
|
|
66
|
+
action="store_true",
|
|
67
|
+
help="Parse the text as a regular expression.",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"-R",
|
|
72
|
+
"--recursive",
|
|
73
|
+
dest="recursive",
|
|
74
|
+
action="store_true",
|
|
75
|
+
help="Look for files matching pattern in subfolders too.",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
parser.add_argument(
|
|
79
|
+
"-e",
|
|
80
|
+
"--eval",
|
|
81
|
+
dest="eval",
|
|
82
|
+
action="store_true",
|
|
83
|
+
help="Eval the replacement as Python code before applying it.",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return parser
|
|
87
|
+
|
|
88
|
+
def _walk_recursive(self):
|
|
89
|
+
for root, dirnames, filenames in os.walk(os.getcwd()):
|
|
90
|
+
for filename in filenames:
|
|
91
|
+
yield os.path.join(root, filename)
|
|
92
|
+
|
|
93
|
+
def _walk_flat(self):
|
|
94
|
+
root = os.getcwd()
|
|
95
|
+
for filename in os.listdir(root):
|
|
96
|
+
yield os.path.join(root, filename)
|
|
97
|
+
|
|
98
|
+
def _replace_regex(self, line, text, replacement):
|
|
99
|
+
return re.sub(text, replacement, line)
|
|
100
|
+
|
|
101
|
+
def _replace_plain(self, line, text, replacement):
|
|
102
|
+
return line.replace(text, replacement)
|
|
103
|
+
|
|
104
|
+
def _match_regex(self, line, text):
|
|
105
|
+
return re.search(text, line) is not None
|
|
106
|
+
|
|
107
|
+
def _match_plain(self, line, text):
|
|
108
|
+
return text in line
|
|
109
|
+
|
|
110
|
+
def take_action(self, opts):
|
|
111
|
+
walk = self._walk_flat
|
|
112
|
+
if opts.recursive:
|
|
113
|
+
walk = self._walk_recursive
|
|
114
|
+
|
|
115
|
+
match = self._match_plain
|
|
116
|
+
if opts.regex:
|
|
117
|
+
match = self._match_regex
|
|
118
|
+
|
|
119
|
+
replace = self._replace_plain
|
|
120
|
+
if opts.regex:
|
|
121
|
+
replace = self._replace_regex
|
|
122
|
+
|
|
123
|
+
matches = []
|
|
124
|
+
for filepath in walk():
|
|
125
|
+
if fnmatch.fnmatch(filepath, opts.pattern):
|
|
126
|
+
matches.append(filepath)
|
|
127
|
+
|
|
128
|
+
print("%s files matching" % len(matches))
|
|
129
|
+
for filepath in matches:
|
|
130
|
+
replacement = opts.replacement
|
|
131
|
+
if opts.eval and replacement:
|
|
132
|
+
replacement = str(eval(replacement, globals()))
|
|
133
|
+
|
|
134
|
+
addition = opts.addition
|
|
135
|
+
if opts.eval and addition:
|
|
136
|
+
addition = str(eval(addition, globals()))
|
|
137
|
+
|
|
138
|
+
matches = False
|
|
139
|
+
lines = []
|
|
140
|
+
with open(filepath) as f:
|
|
141
|
+
for line in f:
|
|
142
|
+
if not match(line, opts.text):
|
|
143
|
+
lines.append(line)
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
matches = True
|
|
147
|
+
empty_line = not line.strip()
|
|
148
|
+
if opts.replacement:
|
|
149
|
+
line = replace(line, opts.text, replacement)
|
|
150
|
+
|
|
151
|
+
if empty_line or line.strip() and not opts.delete:
|
|
152
|
+
lines.append(line)
|
|
153
|
+
|
|
154
|
+
if opts.addition:
|
|
155
|
+
lines.append(addition + "\n")
|
|
156
|
+
|
|
157
|
+
print("%s Patching %s" % (matches and "!" or "x", filepath))
|
|
158
|
+
if matches:
|
|
159
|
+
with open(filepath, "w") as f:
|
|
160
|
+
f.writelines(lines)
|