diff options
author | Matus Valo <matusvalo@users.noreply.github.com> | 2022-11-08 22:35:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-08 21:35:59 +0000 |
commit | 327b87a240b8e03fb333c7ff8ad36b576fd774f0 (patch) | |
tree | 58486e39826c9654d97880eb87e334ecbe9d9eaf | |
parent | 2d40828375f2a25a4b931e582bf0ad5161926e42 (diff) | |
download | cython-327b87a240b8e03fb333c7ff8ad36b576fd774f0.tar.gz |
cython, cythonize commands print a specific error when file does not exist (#4629)
-rw-r--r-- | Cython/Build/Cythonize.py | 17 | ||||
-rw-r--r-- | Cython/Compiler/CmdLine.py | 10 | ||||
-rw-r--r-- | Cython/Compiler/Main.py | 13 | ||||
-rw-r--r-- | Cython/Compiler/Tests/TestCmdLine.py | 12 | ||||
-rw-r--r-- | Tools/ci-run.sh | 4 | ||||
-rw-r--r-- | test-requirements-27.txt | 1 | ||||
-rw-r--r-- | test-requirements-pypy27.txt | 2 | ||||
-rw-r--r-- | tests/run/cython_no_files.srctree | 34 |
8 files changed, 86 insertions, 7 deletions
diff --git a/Cython/Build/Cythonize.py b/Cython/Build/Cythonize.py index ab18a12bc..179c04060 100644 --- a/Cython/Build/Cythonize.py +++ b/Cython/Build/Cythonize.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from __future__ import absolute_import +from __future__ import absolute_import, print_function import os import shutil @@ -45,10 +45,12 @@ def find_package_base(path): package_path = '%s/%s' % (parent, package_path) return base_dir, package_path - def cython_compile(path_pattern, options): - pool = None all_paths = map(os.path.abspath, extended_iglob(path_pattern)) + _cython_compile_files(all_paths, options) + +def _cython_compile_files(all_paths, options): + pool = None try: for path in all_paths: if options.build_inplace: @@ -230,8 +232,15 @@ def parse_args(args): def main(args=None): options, paths = parse_args(args) + all_paths = [] for path in paths: - cython_compile(path, options) + expanded_path = [os.path.abspath(p) for p in extended_iglob(path)] + if not expanded_path: + import sys + print("{}: No such file or directory: '{}'".format(sys.argv[0], path), file=sys.stderr) + sys.exit(1) + all_paths.extend(expanded_path) + _cython_compile_files(all_paths, options) if __name__ == '__main__': diff --git a/Cython/Compiler/CmdLine.py b/Cython/Compiler/CmdLine.py index 80a0cc99d..c330fcc05 100644 --- a/Cython/Compiler/CmdLine.py +++ b/Cython/Compiler/CmdLine.py @@ -4,11 +4,17 @@ from __future__ import absolute_import +import sys import os from argparse import ArgumentParser, Action, SUPPRESS from . import Options +if sys.version_info < (3, 3): + # TODO: This workaround can be removed in Cython 3.1 + FileNotFoundError = IOError + + class ParseDirectivesAction(Action): def __call__(self, parser, namespace, values, option_string=None): old_directives = dict(getattr(namespace, self.dest, @@ -209,6 +215,10 @@ def parse_command_line_raw(parser, args): def parse_command_line(args): parser = create_cython_argparser() arguments, sources = parse_command_line_raw(parser, args) + for source in sources: + if not os.path.exists(source): + import errno + raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), source) options = Options.CompilationOptions(Options.default_options) for name, value in vars(arguments).items(): diff --git a/Cython/Compiler/Main.py b/Cython/Compiler/Main.py index eecd49feb..d5985457d 100644 --- a/Cython/Compiler/Main.py +++ b/Cython/Compiler/Main.py @@ -2,7 +2,7 @@ # Cython Top Level # -from __future__ import absolute_import +from __future__ import absolute_import, print_function import os import re @@ -747,7 +747,16 @@ def main(command_line = 0): args = sys.argv[1:] any_failures = 0 if command_line: - options, sources = parse_command_line(args) + try: + options, sources = parse_command_line(args) + except IOError as e: + # TODO: IOError can be replaced with FileNotFoundError in Cython 3.1 + import errno + if errno.ENOENT != e.errno: + # Raised IOError is not caused by missing file. + raise + print("{}: No such file or directory: '{}'".format(sys.argv[0], e.filename), file=sys.stderr) + sys.exit(1) else: options = CompilationOptions(default_options) sources = args diff --git a/Cython/Compiler/Tests/TestCmdLine.py b/Cython/Compiler/Tests/TestCmdLine.py index 6c74fe3a2..0961dfa03 100644 --- a/Cython/Compiler/Tests/TestCmdLine.py +++ b/Cython/Compiler/Tests/TestCmdLine.py @@ -3,6 +3,10 @@ import sys import re from unittest import TestCase try: + from unittest.mock import patch, Mock +except ImportError: # Py2 + from mock import patch, Mock +try: from StringIO import StringIO except ImportError: from io import StringIO # doesn't accept 'str' in Py2 @@ -12,7 +16,15 @@ from ..CmdLine import parse_command_line from .Utils import backup_Options, restore_Options, check_global_options +unpatched_exists = os.path.exists + +def patched_exists(path): + # avoid the Cython command raising a file not found error + if path in ('source.pyx', 'file.pyx', 'file1.pyx', 'file2.pyx', 'file3.pyx', 'foo.pyx', 'bar.pyx'): + return True + return unpatched_exists(path) +@patch('os.path.exists', new=Mock(side_effect=patched_exists)) class CmdLineParserTest(TestCase): def setUp(self): self._options_backup = backup_Options() diff --git a/Tools/ci-run.sh b/Tools/ci-run.sh index bd8ef0c8b..f25041415 100644 --- a/Tools/ci-run.sh +++ b/Tools/ci-run.sh @@ -68,12 +68,14 @@ if [[ $PYTHON_VERSION == "2.7"* ]]; then elif [[ $PYTHON_VERSION == "3."[45]* ]]; then python -m pip install wheel || exit 1 python -m pip install -r test-requirements-34.txt || exit 1 +elif [[ $PYTHON_VERSION == "pypy-2.7" ]]; then + pip install wheel || exit 1 + pip install -r test-requirements-pypy27.txt || exit 1 else python -m pip install -U pip "setuptools<60" wheel || exit 1 if [[ $PYTHON_VERSION != *"-dev" || $COVERAGE == "1" ]]; then python -m pip install -r test-requirements.txt || exit 1 - if [[ $PYTHON_VERSION != "pypy"* && $PYTHON_VERSION != "3."[1]* ]]; then python -m pip install -r test-requirements-cpython.txt || exit 1 fi diff --git a/test-requirements-27.txt b/test-requirements-27.txt index efec3bbbf..b518c2570 100644 --- a/test-requirements-27.txt +++ b/test-requirements-27.txt @@ -62,3 +62,4 @@ wcwidth==0.2.5 webencodings==0.5.1 widgetsnbextension==3.5.1 zipp==1.2.0 +mock==3.0.5 diff --git a/test-requirements-pypy27.txt b/test-requirements-pypy27.txt new file mode 100644 index 000000000..9f9505240 --- /dev/null +++ b/test-requirements-pypy27.txt @@ -0,0 +1,2 @@ +-r test-requirements.txt +mock==3.0.5 diff --git a/tests/run/cython_no_files.srctree b/tests/run/cython_no_files.srctree new file mode 100644 index 000000000..455258c03 --- /dev/null +++ b/tests/run/cython_no_files.srctree @@ -0,0 +1,34 @@ +PYTHON test_cythonize_no_files.py +PYTHON test_cython_no_files.py + +######## a.py ########### +a = 1 + +######## b.py ########### +b = 2 + +######## c.pyx ########### +c = 3 + +######## d.pyx ########### +d = 4 + +######## test_cythonize_no_files.py ########### +import subprocess +import sys + +cmd = [sys.executable, '-c', 'from Cython.Build.Cythonize import main; main()', 'a.py', 'b.py', 'c.py', '*.pyx'] +proc = subprocess.Popen(cmd, stderr=subprocess.PIPE) +_, err = proc.communicate() +assert proc.returncode == 1, proc.returncode +assert b"No such file or directory: 'c.py'" in err, err + +######## test_cython_no_files.py ########### +import subprocess +import sys + +cmd = [sys.executable, '-c', 'from Cython.Compiler.Main import main; main(command_line = 1)', 'a.py', 'b.py', 'c.py', '*.pyx'] +proc = subprocess.Popen(cmd, stderr=subprocess.PIPE) +_, err = proc.communicate() +assert proc.returncode == 1, proc.returncode +assert b"No such file or directory: 'c.py'" in err, err |