summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatus Valo <matusvalo@users.noreply.github.com>2022-11-08 22:35:59 +0100
committerGitHub <noreply@github.com>2022-11-08 21:35:59 +0000
commit327b87a240b8e03fb333c7ff8ad36b576fd774f0 (patch)
tree58486e39826c9654d97880eb87e334ecbe9d9eaf
parent2d40828375f2a25a4b931e582bf0ad5161926e42 (diff)
downloadcython-327b87a240b8e03fb333c7ff8ad36b576fd774f0.tar.gz
cython, cythonize commands print a specific error when file does not exist (#4629)
-rw-r--r--Cython/Build/Cythonize.py17
-rw-r--r--Cython/Compiler/CmdLine.py10
-rw-r--r--Cython/Compiler/Main.py13
-rw-r--r--Cython/Compiler/Tests/TestCmdLine.py12
-rw-r--r--Tools/ci-run.sh4
-rw-r--r--test-requirements-27.txt1
-rw-r--r--test-requirements-pypy27.txt2
-rw-r--r--tests/run/cython_no_files.srctree34
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