8000 Merge pull request #25181 from HaoZeke/newGH22819 · numpy/numpy@18c6157 · GitHub
[go: up one dir, main page]

Skip to content

Commit 18c6157

Browse files
authored
Merge pull request #25181 from HaoZeke/newGH22819
BUG: Disallow shadowed modulenames
2 parents 605e345 + 50b0e81 commit 18c6157

File tree

5 files changed

+95
-25
lines changed

5 files changed

+95
-25
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
``f2py`` will no longer accept ambiguous ``-m`` and ``.pyf`` CLI combinations.
2+
When more than one ``.pyf`` file is passed, an error is raised. When both ``-m``
3+
and a ``.pyf`` is passed, a warning is emitted and the ``-m`` provided name is
4+
ignored.

doc/source/f2py/usage.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,11 @@ Other options
239239
``-m <modulename>``
240240
Name of an extension module. Default is ``untitled``.
241241

242-
.. warning:: Don't use this option if a signature file (``*.pyf``) is used.
242+
.. warning::
243+
Don't use this option if a signature file (``*.pyf``) is used.
243244

244-
.. versionchanged:: 2.0.0
245-
Will ignore ``-m`` if a ``pyf`` file is provided.
245+
.. versionchanged:: 1.26.3
246+
Will ignore ``-m`` if a ``pyf`` file is provided.
246247

247248
``--[no-]lower``
248249
Do [not] lower the cases in ``<fortran files>``. By default, ``--lower`` is

numpy/f2py/f2py2e.py

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,16 @@ def run_main(comline_list):
450450
f2pydir = os.path.dirname(os.path.abspath(cfuncs.__file__))
451451
fobjhsrc = os.path.join(f2pydir, 'src', 'fortranobject.h')
452452
fobjcsrc = os.path.join(f2pydir, 'src', 'fortranobject.c')
453+
# gh-22819 -- begin
454+
parser = make_f2py_parser()
455+
args, comline_list = parser.parse_known_args(comline_list)
456+
pyf_files, _ = filter_files("", "[.]pyf([.]src|)", comline_list)
457+
# Checks that no existing modulename is defined in a pyf file
458+
# TODO: Remove all this when scaninputline is replaced
459+
if "-h" not in comline_list and args.module_name: # Can't check what doesn't exist yet, -h creates the pyf
460+
modname = validate_modulename(pyf_files, args.module_name)
461+
comline_list += ['-m', modname] # needed for the rest of scaninputline
462+
# gh-22819 -- end
453463
files, options = scaninputline(comline_list)
454464
auxfuncs.options = options
455465
capi_maps.load_f2cmap_file(options['f2cmap_file'])
@@ -516,24 +526,30 @@ def get_prefix(module):
516526
p = os.path.dirname(os.path.dir 8000 name(module.__file__))
517527
return p
518528

519-
def preparse_sysargv():
520-
# To keep backwards bug compatibility, newer flags are handled by argparse,
521-
# and `sys.argv` is passed to the rest of `f2py` as is.
529+
def make_f2py_parser():
522530
parser = argparse.ArgumentParser(add_help=False)
523531
parser.add_argument("--dep", action="append", dest="dependencies")
524532
parser.add_argument("--backend", choices=['meson', 'distutils'], default='distutils')
533+
parser.add_argument("-m", dest="module_name")
534+
return parser
535+
536+
def preparse_sysargv():
537+
# To keep backwards bug compatibility, newer flags are handled by argparse,
538+
# and `sys.argv` is passed to the rest of `f2py` as is.
539+
parser = make_f2py_parser()
525540

526541
args, remaining_argv = parser.parse_known_args()
527542
sys.argv = [sys.argv[0]] + remaining_argv
528543

529544
backend_key = args.backend
530545
if sys.version_info >= (3, 12) and backend_key == 'distutils':
531-
outmess('Cannot use distutils backend with Python 3.12, using meson backend instead.')
546+
outmess("Cannot use distutils backend with Python 3.12, using meson backend instead.\n")
532547
backend_key = 'meson'
533548

534549
return {
535550
"dependencies": args.dependencies or [],
536-
"backend": backend_key
551+
"backend": backend_key,
552+
"modulename": args.module_name,
537553
}
538554

539555
def run_compile():
@@ -544,11 +560,11 @@ def run_compile():
544560

545561
# Collect dependency flags, preprocess sys.argv
546562
argy = preparse_sysargv()
563+
modulename = argy["modulename"]
547564
dependencies = argy["dependencies"]
548565
backend_key = argy["backend"]
549566
build_backend = f2py_build_generator(backend_key)
550567

551-
552568
i = sys.argv.index('-c')
553569
del sys.argv[i]
554570

@@ -628,30 +644,17 @@ def run_compile():
628644
if '--quiet' in f2py_flags:
629645
setup_flags.append('--quiet')
630646

631-
modulename = 'untitled'
632647
sources = sys.argv[1:]
633-
634648
for optname in ['--include_paths', '--include-paths', '--f2cmap']:
635649
if optname in sys.argv:
636650
i = sys.argv.index(optname)
637651
f2py_flags.extend(sys.argv[i:i + 2])
638652
del sys.argv[i + 1], sys.argv[i]
639653
sources = sys.argv[1:]
640654

641-
pyf_files = []
642-
if '-m' in sys.argv:
643-
i = sys.argv.index('-m')
644-
modulename = sys.argv[i + 1]
645-
del sys.argv[i + 1], sys.argv[i]
646-
sources = sys.argv[1:]
647-
else:
648-
pyf_files, _sources = filter_files('', '[.]pyf([.]src|)', sources)
649-
sources = pyf_files + _sources
650-
for f in pyf_files:
651-
modulename = auxfuncs.get_f2py_modulename(f)
652-
if modulename:
653-
break
654-
655+
pyf_files, _sources = filter_files("", "[.]pyf([.]src|)", sources)
656+
sources = pyf_files + _sources
657+
modulename = validate_modulename(pyf_files, modulename)
655658
extra_objects, sources = filter_files('', '[.](o|a|so|dylib)', sources)
656659
include_dirs, sources = filter_files('-I', '', sources, remove_prefix=1)
657660
library_dirs, sources = filter_files('-L', '', sources, remove_prefix=1)
@@ -698,6 +701,21 @@ def run_compile():
698701

699702
builder.compile()
700703

704+
705+
def validate_modulename(pyf_files, modulename='untitled'):
706+
if len(pyf_files) > 1:
707+
raise ValueError("Only one .pyf file per call")
708+
if pyf_files:
709+
pyff = pyf_files[0]
710+
pyf_modname = auxfuncs.get_f2py_modulename(pyff)
711+
if modulename != pyf_modname:
712+
outmess(
713+
f"Ignoring -m {modulename}.\n"
714+
f"{pyff} defines {pyf_modname} to be the modulename.\n"
715+
)
716+
modulename = pyf_modname
717+
return modulename
718+
701719
def main():
702720
if '--help-link' in sys.argv[1:]:
703721
sys.argv.remove('--help-link')

numpy/f2py/tests/src/cli/gh_22819.pyf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
python module test_22819
2+
interface
3+
subroutine hello()
4+
end subroutine hello
5+
end interface
6+
end python module test_22819

numpy/f2py/tests/test_f2py2e.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ def gh23598_warn(tmpdir_factory):
7171
return fn
7272

7373

74+
@pytest.fixture(scope="session")
75+
def gh22819_cli(tmpdir_factory):
76+
"""F90 file for testing disallowed CLI arguments in ghff819"""
77+
fdat = util.getpath("tests", "src", "cli", "gh_22819.pyf").read_text()
78+
fn = tmpdir_factory.getbasetemp() / "gh_22819.pyf"
79+
fn.write_text(fdat, encoding="ascii")
80+
return fn
81+
82+
7483
@pytest.fixture(scope="session")
7584
def hello_world_f77(tmpdir_factory):
7685
"""Generates a single f77 file for testing"""
@@ -100,6 +109,38 @@ def f2cmap_f90(tmpdir_factory):
100109
return fn
101110

102111

112+
def test_gh22819_cli(capfd, gh22819_cli, monkeypatch):
113+
"""Check that module names are handled correctly
114+
gh-22819
115+
Essentially, the -m name cannot be used to import the module, so the module
116+
named in the .pyf needs to be used instead
117+
118+
CLI :: -m and a .pyf file
119+
"""
120+
ipath = Path(gh22819_cli)
121+
monkeypatch.setattr(sys, "argv", f"f2py -m blah {ipath}".split())
122+
with util.switchdir(ipath.parent):
123+
f2pycli()
124+
gen_paths = [item.name for item in ipath.parent.rglob("*") if item.is_file()]
125+
assert "blahmodule.c" not in gen_paths # shouldn't be generated
126+
assert "blah-f2pywrappers.f" not in gen_paths
127+
assert "test_22819-f2pywrappers.f" in gen_paths
128+
assert "test_22819module.c" in gen_paths
129+
assert "Ignoring blah"
130+
131+
132+
def test_gh22819_many_pyf(capfd, gh22819_cli, monkeypatch):
133+
"""Only one .pyf file allowed
134+
gh-22819
135+
CLI :: .pyf files
136+
"""
137+
ipath = Path(gh22819_cli)
138+
monkeypatch.setattr(sys, "argv", f"f2py -m blah {ipath} hello.pyf".split())
139+
with util.switchdir(ipath.parent):
140+
with pytest.raises(ValueError, match="Only one .pyf file per call"):
141+
f2pycli()
142+
143+
103144
def test_gh23598_warn(capfd, gh23598_warn, monkeypatch):
104145
foutl = get_io_paths(gh23598_warn, mname="test")
105146
ipath = foutl.f90inp

0 commit comments

Comments
 (0)
0