Skip to content
Merged
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 292
run: tools/check-test-outcome.py "$RUNNER_TEMP/pytest.log" --min-tests 293

# 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
21 changes: 14 additions & 7 deletions parser/object_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,18 @@ def _role(fn_name: str) -> str:
_OONAME_OVERRIDES: dict[str, str] = {}


def _strip_class_token(fn_name: str, cls: str) -> str:
def _strip_class_token(fn_name: str, cls: str, prefix: str = "") -> str:
"""Drop the class prefix the function-name object model encodes, leaving
the bare member name. Tries the class's own lower-cased token first, then
the generic superclass tokens, longest match wins."""
tokens = sorted({cls.lower(), *_GENERIC_TOKENS}, key=len, reverse=True)
the bare member name. Tries the prefix the classifier matched and the
class's own lower-cased token, then the generic superclass tokens, longest
match wins.

The matched prefix is what a class whose C prefix is not its lower-cased
name is reached by — `geom_*` for Geometry, `geog_*` for Geography — and
without it those members keep the prefix twice over, as `geomBuffer` on a
Geometry."""
tokens = sorted({cls.lower(), prefix, *_GENERIC_TOKENS} - {""},
key=len, reverse=True)
for tok in tokens:
if fn_name == tok:
return ""
Expand All @@ -241,11 +248,11 @@ def _camel(member: str) -> str:
return "".join(out)


def _ooname(fn_name: str, cls: str) -> str:
def _ooname(fn_name: str, cls: str, prefix: str = "") -> str:
"""Canonical camelCase OO method name for a classified function."""
if fn_name in _OONAME_OVERRIDES:
return _OONAME_OVERRIDES[fn_name]
return _camel(_strip_class_token(fn_name, cls))
return _camel(_strip_class_token(fn_name, cls, prefix))


def _oo_excluded(fn: dict, role: str) -> bool:
Expand Down Expand Up @@ -533,7 +540,7 @@ def attach_object_model(idl: dict, path: Path,
role = _role(name)
method = {"function": name, "role": role,
"scope": tgt["scope"], "backing": name,
"ooName": _ooname(name, cls)}
"ooName": _ooname(name, cls, pref)}
if _oo_excluded(fn, role):
method["ooExclude"] = True
sugar = []
Expand Down
17 changes: 17 additions & 0 deletions tests/test_object_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,23 @@ def test_internal_api_methods_are_excluded(self):
self.assertNotIn("ooExclude", meths["temporal_num_instants"])
self.assertTrue(meths["temporal_inst_n"].get("ooExclude"))

def test_ooname_drops_the_prefix_the_classifier_matched(self):
# A class reached by a prefix that is not its lower-cased name —
# `geom_*` for Geometry, `geog_*` for Geography, `geoset_*` for
# GeomSet — otherwise keeps that prefix in the member name, so the
# method reads `geometry.geomBuffer()`.
om = attach_object_model({"functions": [
{"name": "geom_from_hexewkb"}, {"name": "geog_from_hexewkb"},
{"name": "geoset_start_value"}, {"name": "set_set_subspan"},
]}, MODEL, None)["objectModel"]
names = {m["function"]: m["ooName"]
for s in om["classes"].values() for m in s["methods"]}
self.assertEqual(names["geom_from_hexewkb"], "fromHexewkb")
self.assertEqual(names["geog_from_hexewkb"], "fromHexewkb")
self.assertEqual(names["geoset_start_value"], "startValue")
# One token is dropped, not every repetition of it.
self.assertEqual(names["set_set_subspan"], "setSubspan")

def test_class_ctype_comes_from_the_receiver(self):
# A receiver-role method takes the value it is called on first, so its
# pointee names what the class's instances are — which is what a
Expand Down
Loading