Since yet another sentinel singleton sounds like a dead end, I suggest to use [arg=value] syntax and require a default value in the prototype, as currently required for *optional keyword* arguments.
"[...]" syntax for optional parameter is commonly used in Python documentation (but the exact syntax for multiple optional arguments is different than what I propose, see below). I already saw this syntax for optional parameters in C and PHP languages at least. Python prototype of the standard library and their generated signature: def bool([x=False]) => bool([x]) def bytearray([source=None], [encoding=None], errors="strict") => bytearray([source], [encoding], [errors]) # in practice, default value of 'default' parameter (and maybe also 'key'?) # will more likely be a custom sentinel def sum(iterable, *args, [key=None], [default=None]) => sum(iterable, *args, [key], [default]) # "/" is an hypothetical marker for positional-only arguments def str.replace(old, new, [count=-1], /) => str.replace(old, new, [count], /) def pickle.dump(obj, file, [protocol=3], *, fix_imports=True) => pickle.dump(obj, file, [protocol], *, fix_imports=True) An alternative for generated signature of multiple optional arguments is "bytearray([source[, encoding[, errors]]])", but I'm not a big fan of nested [...], IMHO it's harder to read. And I like the idea of having a signature closer to the actual Python code. Invalid syntaxes raising SyntaxError: * no default value: "def func([x]): ..." * optional arguments before *args: "def func(arg, [opt=None], *args):" In practice, calling a function without an optional argument or pass the (private?) sentinel as the optional argument should have the same behaviour. Each module is free to decide how the sentinel is exposed or not. For example, the inspect module has 2 sentinels: _empty is exposed as Signature.empty and Parameter.empty, whereas _void is private. If I understood correctly, Argument Clinic already supports optional positional arguments, and so doesn't need to be changed. I'm not sure that it's useful for optional keyword-only arguments: def func(*, [arg=None]) => func(*, [arg]) The only difference with optional keyword-only arguments with a default value is the signature: def func(*, arg=None) => func(*, arg=None) See also the discussion on converting the bisect functions to Argument Clinic and issues with the generated signature: http://bugs.python.org/issue28754 Victor _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/