Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
# carries, or a change to them is not exercised until after it merges.
# Consumers use the action; this repository owns the rules.
- name: Refuse a skip, and a suite that shrank
run: tools/check-test-outcome.py "$RUNNER_TEMP/pytest.log" --min-tests 317
run: tools/check-test-outcome.py "$RUNNER_TEMP/pytest.log" --min-tests 318

# The rules earn their place by refusing a log that carries what they
# name. Both fixtures are written here rather than tracked, and the
Expand Down
9 changes: 7 additions & 2 deletions parser/nullable.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@
_PARAM = re.compile(
r'@param\[[^\]]*\]\s+(?P<names>\w+(?:\s*,\s*\w+)*)\s+(?P<desc>.*?)'
r'(?=\n\s*\*\s*@|\*/|\Z)', re.S)
_NULLISH = re.compile(r'may be\s+`?NULL`?|can be\s+`?NULL`?|`?NULL`?\s+is allowed'
r'|or\s+`?NULL`?', re.I)
# Doxygen marks up a parameter or literal it names — ``NULL``, `NULL`, @p NULL,
# \p NULL — and every one of those spellings is the same note. Reading only the
# bare and backticked forms drops the ones an author marked up, and a parameter
# whose note is unread reaches every binding as one that must be supplied.
_MARKED_NULL = r'(?:[@\\]p\s+)?`?NULL`?'
_NULLISH = re.compile(rf'may be\s+{_MARKED_NULL}|can be\s+{_MARKED_NULL}'
rf'|{_MARKED_NULL}\s+is allowed|or\s+{_MARKED_NULL}', re.I)


def extract_nullable(meos_root: str | Path) -> dict[str, list[str]]:
Expand Down
21 changes: 21 additions & 0 deletions tests/test_nullable.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@
{
return NULL;
}

/**
* @brief Interpolate between two geography points
* @param[in] p1 First point
* @param[in] s Spheroid, may be @p NULL when the sphere is meant
* @param[in] tol Tolerance, or \\p NULL for the default
*/
int
interpolate_point4d_spheroid(const POINT4D *p1, const SPHEROID *s,
const double *tol)
{
return 0;
}
'''


Expand All @@ -57,6 +70,14 @@ def test_extracts_only_may_be_null_params(self):
# `temp`, `inst`, `interp` carry no NULL note -> not nullable
self.assertNotIn("temp", nul["temporal_as_mfjson"])

def test_a_null_note_reads_through_the_markup_around_it(self):
# Doxygen marks up the literal it names, and `NULL`, @p NULL and \p NULL
# are one note. A parameter whose note goes unread reaches every binding
# as one that must be supplied.
nul = extract_nullable(self.tmp.name)
self.assertEqual(nul["interpolate_point4d_spheroid"], ["s", "tol"])
self.assertNotIn("p1", nul["interpolate_point4d_spheroid"])

def test_merge_only_existing_params(self):
idl = {"functions": [
{"name": "temporal_as_mfjson",
Expand Down
Loading