From 7e65e2161e859050e2782c9ff9ebd3f671f038e8 Mon Sep 17 00:00:00 2001 From: Stephen L Date: Sun, 30 Oct 2016 04:52:40 +0100 Subject: [PATCH 1/5] Fix --file argument + fix unit tests Signed-off-by: Stephen L. --- pymake/_main.py | 16 +++++++++++----- pymake/_pymake.py | 9 +++++---- pymake/tests/tests_main.py | 5 ++--- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pymake/_main.py b/pymake/_main.py index 5cca7f4..1bd84c3 100644 --- a/pymake/_main.py +++ b/pymake/_main.py @@ -10,7 +10,6 @@ -n, --just-print Don't actually run any commands; just print them (dry-run, recon) -i, --ignore-errors Ignore errors from commands. -Arguments: -f FILE, --file FILE Read FILE as a makefile (makefile) [default: Makefile] """ @@ -20,16 +19,23 @@ PymakeKeyError from ._version import __version__ # NOQA from docopt import docopt +import shlex import sys __all__ = ["main"] -def main(): - opts = docopt(__doc__, version=__version__) - # Filename of the makefile - fpath = opts['--file'] +def main(argv=None): + if argv is None: # if argv is empty, fetch from the commandline + argv = sys.argv[1:] + elif isinstance(argv, basestring): # else if it's a string, parse it + argv = shlex.split(argv) + + # Parse arguments using docopt + opts = docopt(__doc__, argv=argv, version=__version__) + # Filename of the makefile (default: Makefile of current dir) + fpath = opts.get('--file', 'Makefile') # Parse the makefile, substitute the aliases and extract the commands commands, default_alias = parse_makefile_aliases(fpath) diff --git a/pymake/_pymake.py b/pymake/_pymake.py index 8b5a46d..153744d 100644 --- a/pymake/_pymake.py +++ b/pymake/_pymake.py @@ -46,13 +46,14 @@ def parse_makefile_aliases(filepath): with io.open(filepath, mode='r') as fd: ini_str = ini_str + RE_MAKE_CMD.sub('\t', fd.read()) + # Substitute macros macros = dict(RE_MACRO_DEF.findall(ini_str)) - # allow finite amount of nesting - for _ in range(99): + for _ in range(99): # allow finite amount of nesting for (m, expr) in macros.iteritems(): - ini_str = re.sub(r"\$\(" + m + "\)", expr, ini_str, flags=re.M) + remacros = re.compile(r"\$\(" + m + "\)", flags=re.M) + ini_str = remacros.sub(expr, ini_str) if not RE_MACRO.match(ini_str): - # remove macro definitions from rest of parsing + # Remove macro definitions from rest of parsing ini_str = RE_MACRO_DEF.sub("", ini_str) break else: diff --git a/pymake/tests/tests_main.py b/pymake/tests/tests_main.py index b006f4d..557de36 100644 --- a/pymake/tests/tests_main.py +++ b/pymake/tests/tests_main.py @@ -21,14 +21,13 @@ def test_main(): """ Test execution """ fname = os.path.join(os.path.abspath(repeat(os.path.dirname, 3, __file__)), - "examples", "Makefile") + "examples", "Makefile").replace('\\', '/') res = _sh(sys.executable, '-c', 'from pymake import main; import sys; ' + 'sys.argv = ["", "-f", "' + fname + '"]; main()', stderr=subprocess.STDOUT) # actual test: - assert ("hello world" in res) # semi-fake test which gets coverage: @@ -65,7 +64,7 @@ def test_main(): try: main() except OSError as e: - if 'no such file' not in str(e).lower(): + if 'error 2' not in str(e).lower(): # no such file error raise else: raise PymakeTypeError('err') From ce8bc4df1eb0dde934c979656a760560b6a0a378 Mon Sep 17 00:00:00 2001 From: Stephen L Date: Sun, 30 Oct 2016 04:59:46 +0100 Subject: [PATCH 2/5] Fix unit test on Windows and other languages (error message gets translated...) Signed-off-by: Stephen L. --- pymake/tests/tests_main.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pymake/tests/tests_main.py b/pymake/tests/tests_main.py index 557de36..ef0f7cc 100644 --- a/pymake/tests/tests_main.py +++ b/pymake/tests/tests_main.py @@ -63,9 +63,8 @@ def test_main(): sys.argv = ['', '-s', '-f', fname, 'err'] try: main() - except OSError as e: - if 'error 2' not in str(e).lower(): # no such file error - raise + except (OSError, WindowsError) as e: + pass # test passed if file not found else: raise PymakeTypeError('err') From 9748a84f32459960479d5f0d7265570b7f92e837 Mon Sep 17 00:00:00 2001 From: Stephen L Date: Sun, 30 Oct 2016 05:03:12 +0100 Subject: [PATCH 3/5] Fix again unit test OSError... Signed-off-by: Stephen L. --- pymake/tests/tests_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymake/tests/tests_main.py b/pymake/tests/tests_main.py index ef0f7cc..d18570c 100644 --- a/pymake/tests/tests_main.py +++ b/pymake/tests/tests_main.py @@ -63,7 +63,7 @@ def test_main(): sys.argv = ['', '-s', '-f', fname, 'err'] try: main() - except (OSError, WindowsError) as e: + except OSError as e: pass # test passed if file not found else: raise PymakeTypeError('err') From e7cbe64022c8f137b2deef3e600aea8380ddf476 Mon Sep 17 00:00:00 2001 From: Stephen L Date: Sun, 30 Oct 2016 05:09:48 +0100 Subject: [PATCH 4/5] Fix py3 unit test Signed-off-by: Stephen L. --- pymake/_pymake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymake/_pymake.py b/pymake/_pymake.py index 153744d..adeba2c 100644 --- a/pymake/_pymake.py +++ b/pymake/_pymake.py @@ -49,7 +49,7 @@ def parse_makefile_aliases(filepath): # Substitute macros macros = dict(RE_MACRO_DEF.findall(ini_str)) for _ in range(99): # allow finite amount of nesting - for (m, expr) in macros.iteritems(): + for (m, expr) in macros.items(): remacros = re.compile(r"\$\(" + m + "\)", flags=re.M) ini_str = remacros.sub(expr, ini_str) if not RE_MACRO.match(ini_str): From f940f353fa54da41699a4f5c5e5e4a3cc8f8f337 Mon Sep 17 00:00:00 2001 From: Stephen L Date: Sun, 30 Oct 2016 06:06:40 +0100 Subject: [PATCH 5/5] Demonstrating py2.6 shlex madness Signed-off-by: Stephen L. --- pymake/_main.py | 2 +- pymake/_pymake.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pymake/_main.py b/pymake/_main.py index 1bd84c3..5cde0b7 100644 --- a/pymake/_main.py +++ b/pymake/_main.py @@ -30,7 +30,7 @@ def main(argv=None): if argv is None: # if argv is empty, fetch from the commandline argv = sys.argv[1:] elif isinstance(argv, basestring): # else if it's a string, parse it - argv = shlex.split(argv) + argv = shlex.split(argv.encode('ascii')) # Parse arguments using docopt opts = docopt(__doc__, argv=argv, version=__version__) diff --git a/pymake/_pymake.py b/pymake/_pymake.py index adeba2c..2e30b2d 100644 --- a/pymake/_pymake.py +++ b/pymake/_pymake.py @@ -5,6 +5,7 @@ # import compatibility functions and utilities from ._utils import ConfigParser, StringIO import io +import os import re from subprocess import check_call import shlex @@ -43,7 +44,7 @@ def parse_makefile_aliases(filepath): # -- Parsing the Makefile using ConfigParser # Adding a fake section to make the Makefile a valid Ini file ini_str = '[root]\n' - with io.open(filepath, mode='r') as fd: + with io.open(os.path.normpath(filepath), mode='r') as fd: ini_str = ini_str + RE_MAKE_CMD.sub('\t', fd.read()) # Substitute macros @@ -148,9 +149,15 @@ def execute_makefile_commands( return for cmd in cmds: + # Strip null bytes and carriage return + cmd = cmd.rstrip(' \t\r\n\0') + cmd = cmd.rstrip('\r\n\0') # Parse string in a shell-like fashion # (incl quoted strings and comments) parsed_cmd = shlex.split(cmd, comments=True) + # Fix unicode support in shlex of Py2.6... + parsed_cmd = [x.strip('\x00') for x in parsed_cmd] + print(parsed_cmd) # Execute command if not empty (ie, not just a comment) if parsed_cmd: if not silent: @@ -158,6 +165,6 @@ def execute_makefile_commands( # Launch the command and wait to finish (synchronized call) try: check_call(parsed_cmd) - except: + except Exception: if not ignore_errors: raise