Skip to content

Commit

Permalink
Merge pull request #574 from Fortran-FOSS-Programmers/extra_mods-as-t…
Browse files Browse the repository at this point in the history
…oml-dict

Use a table for `extra_mods` in TOML config
  • Loading branch information
ZedThree authored Oct 16, 2023
2 parents b5eb0e6 + 79bd6c2 commit 8726c5e
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 68 deletions.
43 changes: 32 additions & 11 deletions docs/user_guide/project_file_options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -844,20 +844,41 @@ modules will be linked to using ``extra_mods``.
extra_mods
^^^^^^^^^^

A list of modules (and their external documentation) which are not
included in the project. An entry takes the form ``module_name:url``
where ``module_name`` is its name as it would appear in a ``use``
statement, and ``url`` is the location of its documentation. Any entity
which uses this module will provide a link to the external documentation
in the same way that it would provide a link to the documentation of a
module in the project.
A list of modules (and their external documentation) which are not included in
the project. For the markdown metadata, an entry takes the form
``module_name:url`` where ``module_name`` is its name as it would appear in a
``use`` statement, and ``url`` is the location of its documentation. For TOML
options, use a table with ``module_name = "url"`` as the key-value pairs. Any
entity which uses this module will provide a link to the external documentation
in the same way that it would provide a link to the documentation of a module in
the project.

.. tab:: fpm.toml

.. code:: toml
# As an inline table:
extra_mods = { example_mod = "https://example.com" }
# Or you might find it easier as a separate table:
[extra.ford.extra_mods]
iso_fortran_env = "https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html"
example_mod = "https://example.com"
.. tab:: Markdown metadata

.. code:: yaml
extra_mods: iso_fortran_env: "https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html"
example_mod: https://example.com
.. _option-extra_vartypes:

extra_vartypes
^^^^^^^^^^^^^^

Any extra types of variables which FORD should look for. This can be
A list of extra types of variables which FORD should look for. This can be
useful when using, for example, the PETSc library.

.. _option-hide_undoc:
Expand Down Expand Up @@ -911,9 +932,9 @@ page. (*default*: 10)
md_extensions
^^^^^^^^^^^^^

The name of any Markdown extensions which you wish to be used when
parsing your documentation. For example, ``markdown.extensions.toc``. Note
that Markdown-Extra, CodeHilite, and Meta-Data are loaded by default.
List of Markdown extensions which you wish to be used when parsing your
documentation. For example, ``markdown.extensions.toc``. Note that
Markdown-Extra, CodeHilite, and Meta-Data are loaded by default.

.. _option-print_creation_date:

Expand Down
1 change: 1 addition & 0 deletions example/src/ford_test_program.f90
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
program ford_test_program
!! Simple Fortran program to demonstrate the usage of FORD and to test its installation
use iso_fortran_env, only: output_unit, real64
use json_module
implicit none
real (real64) :: global_pi = acos(-1)
!! a global variable, initialized to the value of pi
Expand Down
33 changes: 4 additions & 29 deletions ford/fortran_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,6 @@
from ford._typing import PathLike


INTRINSIC_MODS = {
"iso_fortran_env": "http://fortranwiki.org/fortran/show/iso_fortran_env",
"iso_c_binding": "http://fortranwiki.org/fortran/show/iso_c_binding",
"ieee_arithmetic": "http://fortranwiki.org/fortran/show/ieee_arithmetic",
"ieee_exceptions": "http://fortranwiki.org/fortran/show/IEEE+arithmetic",
"ieee_features": "http://fortranwiki.org/fortran/show/IEEE+arithmetic",
"openacc": "https://www.openacc.org/sites/default/files/inline-images/Specification/OpenACC.3.0.pdf#page=85",
"omp_lib": "https://www.openmp.org/spec-html/5.1/openmpch3.html#x156-1890003",
"mpi": "http://www.mpi-forum.org/docs/mpi-3.1/mpi31-report/node410.htm",
"mpi_f08": "http://www.mpi-forum.org/docs/mpi-3.1/mpi31-report/node409.htm",
}


LINK_TYPES = {
"module": "modules",
"extmodule": "extModules",
Expand Down Expand Up @@ -271,23 +258,11 @@ def correlate(self):
Associates various constructs with each other.
"""

non_local_mods = INTRINSIC_MODS.copy()
for item in self.settings.extra_mods:
if not item:
continue
try:
name, url = item.split(":", 1)
except ValueError:
raise ValueError(
f"Could not parse 'extra_mods' item in project settings: '{item}'\n"
"Expected something of the form 'module_name: http://link.to/module'"
)
name = name.strip()
url = url.strip().strip(r"\"'").strip()
non_local_mods[name.lower()] = f'<a href="{url}">{name}</a>'

self.extModules.extend(
[ExternalModule(name, url) for name, url in non_local_mods.items()]
[
ExternalModule(name, url)
for name, url in self.settings.extra_mods.items()
]
)

# load external FORD FortranModules
Expand Down
40 changes: 35 additions & 5 deletions ford/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@

FAVICON_PATH = Path("favicon.png")

INTRINSIC_MODS = {
"iso_fortran_env": "http://fortranwiki.org/fortran/show/iso_fortran_env",
"iso_c_binding": "http://fortranwiki.org/fortran/show/iso_c_binding",
"ieee_arithmetic": "http://fortranwiki.org/fortran/show/ieee_arithmetic",
"ieee_exceptions": "http://fortranwiki.org/fortran/show/IEEE+arithmetic",
"ieee_features": "http://fortranwiki.org/fortran/show/IEEE+arithmetic",
"openacc": "https://www.openacc.org/sites/default/files/inline-images/Specification/OpenACC.3.0.pdf#page=85",
"omp_lib": "https://www.openmp.org/spec-html/5.1/openmpch3.html#x156-1890003",
"mpi": "http://www.mpi-forum.org/docs/mpi-3.1/mpi31-report/node410.htm",
"mpi_f08": "http://www.mpi-forum.org/docs/mpi-3.1/mpi31-report/node409.htm",
}

# Mapping from key to separator for settings that are dicts. Required
# due to the legacy format
OPTION_SEPARATORS = {
"alias": "=",
"external": "=",
"extra_mods": ":",
"extra_vartypes": ":",
}


def default_cpus() -> int:
try:
Expand Down Expand Up @@ -121,7 +142,7 @@ class ProjectSettings:
external: Dict[str, str] = field(default_factory=dict)
externalize: bool = False
extra_filetypes: Dict[str, ExtraFileType] = field(default_factory=dict)
extra_mods: list = field(default_factory=list)
extra_mods: Dict[str, str] = field(default_factory=dict)
extra_vartypes: list = field(default_factory=list)
facebook: Optional[str] = None
favicon: Path = FAVICON_PATH
Expand Down Expand Up @@ -202,6 +223,7 @@ def __post_init__(self):
self.display = [item.lower() for item in self.display]
self.extensions = list(set(self.extensions) | set(self.fpp_extensions))
self.exclude_dir.append(self.output_dir)
self.extra_mods.update(INTRINSIC_MODS)

# Check that none of the docmarks are the same
docmarks = ["docmark", "predocmark", "docmark_alt", "predocmark_alt"]
Expand Down Expand Up @@ -279,6 +301,7 @@ def load_toml_settings(directory: PathLike) -> Optional[ProjectSettings]:
if "ford" not in settings["extra"]:
return None

print(f"Reading Ford options from {filename.absolute()}")
return ProjectSettings(**settings["extra"]["ford"])


Expand Down Expand Up @@ -347,15 +370,16 @@ def convert_types_from_metapreprocessor(
file_type.extension: file_type for file_type in file_types
}
else:
settings[key] = _parse_to_dict(value, name=key)
sep = OPTION_SEPARATORS[key]
settings[key] = _parse_to_dict(value, name=key, sep=sep)

for key in keys_to_drop:
settings.pop(key)

return settings


def _parse_to_dict(string_list: List[str], name: str) -> Dict[str, str]:
def _parse_to_dict(string_list: List[str], name: str, sep: str) -> Dict[str, str]:
"""Parse a list of strings of form "key = value" into a dict
Parameters
Expand All @@ -364,18 +388,24 @@ def _parse_to_dict(string_list: List[str], name: str) -> Dict[str, str]:
List of strings to parse
name : str
Name in parent settings object, only used for error message
sep: str
Separator between key and value
"""

result = {}
for string in string_list:
try:
key, value = string.split("=", 1)
key, value = string.split(sep, 1)
except ValueError:
raise RuntimeError(
f"Error setting option {name!r}: expected '=' in {string!r}"
f"Error setting option {name!r}: expected '{sep}' in {string!r}"
)

# Remove extraneous quotes for the URL options
if sep == ":":
value.strip().strip(r"\"'")

result[key.strip()] = value.strip()
return result

Expand Down
5 changes: 2 additions & 3 deletions test/test_md_inputs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pathlib
import os.path
import re
import sys
from typing import List, Optional
Expand Down Expand Up @@ -64,7 +63,7 @@ def test_extra_mods_intrinsic(
preprocess: false
"""
# Initial value of intrinsic mods
old_intrinsic_mods = ford.fortran_project.INTRINSIC_MODS.copy()
old_intrinsic_mods = ford.settings.INTRINSIC_MODS.copy()

# set up project data
copy_fortran_file(data)
Expand All @@ -73,7 +72,7 @@ def test_extra_mods_intrinsic(
run_ford(monkeypatch, md_file)

# Check that the module level variable has not been changed
assert ford.fortran_project.INTRINSIC_MODS == old_intrinsic_mods
assert ford.settings.INTRINSIC_MODS == old_intrinsic_mods


def get_main_body_text(html_dir: pathlib.Path, filename: str) -> List[str]:
Expand Down
24 changes: 4 additions & 20 deletions test/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -984,18 +984,12 @@ def test_uses(copy_fortran_file):
end module mod_c
module mod_d
use unquoted_external_module
use quoted_external_module
use external_module
end module mod_d
"""

extra_mods = {
"unquoted_external_module": "http://unquoted.example.com",
"quoted_external_module": '"http://quoted.example.org"',
}

settings = copy_fortran_file(data)
settings.extra_mods = [f"{key}: {value}" for key, value in extra_mods.items()]
settings.extra_mods = {"external_module": "http://example.com"}

project = create_project(settings)
mod_a = project.modules[0]
Expand All @@ -1007,12 +1001,8 @@ def test_uses(copy_fortran_file):
assert mod_b.uses == {mod_a}
assert mod_c.uses == {mod_a, mod_b}

mod_d_links = list(mod_d.uses)
for link in mod_d_links:
soup = BeautifulSoup(link.get_url(), features="html.parser")
link = soup.a
expected_link = extra_mods[soup.text].strip(r"\"'")
assert link["href"] == expected_link, link
for link in mod_d.uses:
assert link.get_url() == "http://example.com"


def test_submodule_uses(copy_fortran_file):
Expand All @@ -1032,13 +1022,7 @@ def test_submodule_uses(copy_fortran_file):
end submodule mod_d
"""

extra_mods = {
"unquoted_external_module": "http://unquoted.example.com",
"quoted_external_module": '"http://quoted.example.org"',
}

settings = copy_fortran_file(data)
settings.extra_mods = [f"{key}: {value}" for key, value in extra_mods.items()]

project = create_project(settings)
mod_a = project.modules[0]
Expand Down

0 comments on commit 8726c5e

Please sign in to comment.