diff --git a/Include/object.h b/Include/object.h --- a/Include/object.h +++ b/Include/object.h @@ -492,8 +492,10 @@ PyAPI_FUNC(unsigned int) PyType_ClearCache(void); PyAPI_FUNC(void) PyType_Modified(PyTypeObject *); +#ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *name, const char *internal_doc); PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_doc); +#endif /* Generic operations on objects */ struct _Py_Identifier; diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -136,7 +136,7 @@ /*[clinic input] module curses -class curses.window +class curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type" [clinic start generated code]*/ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ @@ -605,10 +605,10 @@ {"addch", (PyCFunction)curses_window_addch, METH_VARARGS, curses_window_addch__doc__}, static PyObject * -curses_window_addch_impl(PyObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr); +curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr); static PyObject * -curses_window_addch(PyObject *self, PyObject *args) +curses_window_addch(PyCursesWindowObject *self, PyObject *args) { PyObject *return_value = NULL; int group_left_1 = 0; @@ -650,8 +650,8 @@ } static PyObject * -curses_window_addch_impl(PyObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr) -/*[clinic end generated code: checksum=f6eeada77a9ec085125f3a27e4a2095f2a4c50be]*/ +curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr) +/*[clinic end generated code: checksum=e1cdbd4f4e42fc6b36fd4755d7e4bd5b58751ea1]*/ { PyCursesWindowObject *cwself = (PyCursesWindowObject *)self; int coordinates_group = group_left_1; diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -18,7 +18,7 @@ /*[clinic input] module datetime -class datetime.datetime +class datetime.datetime "PyDateTime_DateTime *" "&PyDateTime_DateTimeType" [clinic start generated code]*/ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ @@ -4171,10 +4171,10 @@ {"now", (PyCFunction)datetime_datetime_now, METH_VARARGS|METH_KEYWORDS|METH_CLASS, datetime_datetime_now__doc__}, static PyObject * -datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz); +datetime_datetime_now_impl(PyDateTime_DateTime *type, PyObject *tz); static PyObject * -datetime_datetime_now(PyTypeObject *type, PyObject *args, PyObject *kwargs) +datetime_datetime_now(PyDateTime_DateTime *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; static char *_keywords[] = {"tz", NULL}; @@ -4191,8 +4191,8 @@ } static PyObject * -datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) -/*[clinic end generated code: checksum=a6d3ad2c0ab6389075289af3467f7b8eb13f5f5c]*/ +datetime_datetime_now_impl(PyDateTime_DateTime *type, PyObject *tz) +/*[clinic end generated code: checksum=a08bda09f461ab139673f24a022c728b9b2c6e0e]*/ { PyObject *self; diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -30,7 +30,7 @@ /*[clinic input] module dbm -class dbm.dbm +class dbm.dbm "dbmobject *" "&Dbmtype" [clinic start generated code]*/ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ diff --git a/Modules/_pickle.c b/Modules/_pickle.c --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -6,28 +6,13 @@ /*[clinic input] module _pickle -class _pickle.Pickler -class _pickle.PicklerMemoProxy -class _pickle.Unpickler -class _pickle.UnpicklerMemoProxy +class _pickle.Pickler "PicklerObject *" "&Pickler_Type" +class _pickle.PicklerMemoProxy "PicklerMemoProxyObject *" "&PicklerMemoProxyType" +class _pickle.Unpickler "UnpicklerObject *" "&Unpickler_Type" +class _pickle.UnpicklerMemoProxy "UnpicklerMemoProxyObject *" "&UnpicklerMemoProxyType" [clinic start generated code]*/ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ -/*[python input] -class PicklerObject_converter(self_converter): - type = "PicklerObject *" - -class PicklerMemoProxyObject_converter(self_converter): - type = "PicklerMemoProxyObject *" - -class UnpicklerObject_converter(self_converter): - type = "UnpicklerObject *" - -class UnpicklerMemoProxyObject_converter(self_converter): - type = "UnpicklerMemoProxyObject *" -[python start generated code]*/ -/*[python end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ - /* Bump this when new opcodes are added to the pickle protocol. */ enum { HIGHEST_PROTOCOL = 4, @@ -3878,8 +3863,6 @@ _pickle.Pickler.clear_memo - self: PicklerObject - Clears the pickler's "memo". The memo is the data structure that remembers which objects the @@ -3923,7 +3906,6 @@ _pickle.Pickler.dump - self: PicklerObject obj: object / @@ -4018,7 +4000,6 @@ _pickle.Pickler.__init__ - self: PicklerObject file: object protocol: object = NULL fix_imports: bool = True @@ -4158,8 +4139,6 @@ /*[clinic input] _pickle.PicklerMemoProxy.clear - self: PicklerMemoProxyObject - Remove all items from memo. [clinic start generated code]*/ @@ -4191,8 +4170,6 @@ /*[clinic input] _pickle.PicklerMemoProxy.copy - self: PicklerMemoProxyObject - Copy the memo to a new object. [clinic start generated code]*/ @@ -4254,8 +4231,6 @@ /*[clinic input] _pickle.PicklerMemoProxy.__reduce__ - self: PicklerMemoProxyObject - Implement pickle support. [clinic start generated code]*/ @@ -6310,17 +6285,17 @@ {"load", (PyCFunction)_pickle_Unpickler_load, METH_NOARGS, _pickle_Unpickler_load__doc__}, static PyObject * -_pickle_Unpickler_load_impl(PyObject *self); +_pickle_Unpickler_load_impl(UnpicklerObject *self); static PyObject * -_pickle_Unpickler_load(PyObject *self, PyObject *Py_UNUSED(ignored)) +_pickle_Unpickler_load(UnpicklerObject *self, PyObject *Py_UNUSED(ignored)) { return _pickle_Unpickler_load_impl(self); } static PyObject * -_pickle_Unpickler_load_impl(PyObject *self) -/*[clinic end generated code: checksum=fb1119422c5e03045d690d1cd6c457f1ca4c585d]*/ +_pickle_Unpickler_load_impl(UnpicklerObject *self) +/*[clinic end generated code: checksum=5ccece694e9898856d916e0a87f0133d4537ebb9]*/ { UnpicklerObject *unpickler = (UnpicklerObject*)self; @@ -6347,7 +6322,6 @@ _pickle.Unpickler.find_class - self: UnpicklerObject module_name: object global_name: object / @@ -6551,7 +6525,6 @@ _pickle.Unpickler.__init__ - self: UnpicklerObject file: object * fix_imports: bool = True @@ -6692,8 +6665,6 @@ /*[clinic input] _pickle.UnpicklerMemoProxy.clear - self: UnpicklerMemoProxyObject - Remove all items from memo. [clinic start generated code]*/ @@ -6727,8 +6698,6 @@ /*[clinic input] _pickle.UnpicklerMemoProxy.copy - self: UnpicklerMemoProxyObject - Copy the memo to a new object. [clinic start generated code]*/ @@ -6783,8 +6752,6 @@ /*[clinic input] _pickle.UnpicklerMemoProxy.__reduce__ - self: UnpicklerMemoProxyObject - Implement pickling support. [clinic start generated code]*/ diff --git a/Modules/_sre.c b/Modules/_sre.c --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -528,11 +528,10 @@ /*[clinic input] module _sre -class _sre.SRE_Pattern +class _sre.SRE_Pattern "PatternObject *" "&Pattern_Type" _sre.SRE_Pattern.match as pattern_match - self: self(type="PatternObject *") pattern: object pos: Py_ssize_t = 0 endpos: Py_ssize_t(c_default="PY_SSIZE_T_MAX") = sys.maxsize diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -19,7 +19,7 @@ /*[clinic input] module unicodedata -class unicodedata.UCD +class unicodedata.UCD 'PreviousDBVersion *' '&UCD_Type' [clinic start generated code]*/ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ @@ -140,10 +140,10 @@ {"decimal", (PyCFunction)unicodedata_UCD_decimal, METH_VARARGS, unicodedata_UCD_decimal__doc__}, static PyObject * -unicodedata_UCD_decimal_impl(PyObject *self, PyUnicodeObject *unichr, PyObject *default_value); +unicodedata_UCD_decimal_impl(PreviousDBVersion *self, PyUnicodeObject *unichr, PyObject *default_value); static PyObject * -unicodedata_UCD_decimal(PyObject *self, PyObject *args) +unicodedata_UCD_decimal(PreviousDBVersion *self, PyObject *args) { PyObject *return_value = NULL; PyUnicodeObject *unichr; @@ -160,8 +160,8 @@ } static PyObject * -unicodedata_UCD_decimal_impl(PyObject *self, PyUnicodeObject *unichr, PyObject *default_value) -/*[clinic end generated code: checksum=01826b179d497d8fd3842c56679ecbd4faddaa95]*/ +unicodedata_UCD_decimal_impl(PreviousDBVersion *self, PyUnicodeObject *unichr, PyObject *default_value) +/*[clinic end generated code: checksum=e1371a1a016e19fdd3cd2c1af1d1832df095f50b]*/ { PyUnicodeObject *v = (PyUnicodeObject *)unichr; int have_old = 0; diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -83,8 +83,8 @@ /*[clinic input] module zlib -class zlib.Compress -class zlib.Decompress +class zlib.Compress "compobject *" "&Comptype" +class zlib.Decompress "compobject *" "&Decomptype" [clinic start generated code]*/ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ @@ -748,8 +748,6 @@ zlib.Decompress.decompress - self: self(type="compobject *") - data: Py_buffer The binary data to decompress. max_length: uint = 0 @@ -1030,8 +1028,6 @@ /*[clinic input] zlib.Compress.copy - self: self(type="compobject *") - Return a copy of the compression object. [clinic start generated code]*/ diff --git a/Objects/dictobject.c b/Objects/dictobject.c --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -70,7 +70,7 @@ #include "stringlib/eq.h" /*[clinic input] -class dict +class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ @@ -1710,10 +1710,10 @@ {"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS|METH_CLASS, dict_fromkeys__doc__}, static PyObject * -dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value); +dict_fromkeys_impl(PyDictObject *type, PyObject *iterable, PyObject *value); static PyObject * -dict_fromkeys(PyTypeObject *type, PyObject *args) +dict_fromkeys(PyDictObject *type, PyObject *args) { PyObject *return_value = NULL; PyObject *iterable; @@ -1730,8 +1730,8 @@ } static PyObject * -dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) -/*[clinic end generated code: checksum=008269e1774a379b356841548c04061fd78a9542]*/ +dict_fromkeys_impl(PyDictObject *type, PyObject *iterable, PyObject *value) +/*[clinic end generated code: checksum=86c2e7b5324db1321b80c02092a4e5bce10a5777]*/ { PyObject *it; /* iter(seq) */ PyObject *key; @@ -2217,8 +2217,8 @@ {"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__}, static PyObject * -dict___contains__(PyObject *self, PyObject *key) -/*[clinic end generated code: checksum=c4f85a39baac4776c4275ad5f072f7732c5f0806]*/ +dict___contains__(PyDictObject *self, PyObject *key) +/*[clinic end generated code: checksum=744ca54369dda9815a596304087f1b37fafa5960]*/ { register PyDictObject *mp = (PyDictObject *)self; Py_hash_t hash; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -48,7 +48,7 @@ #endif /*[clinic input] -class str +class str "PyUnicodeObject *" "&PyUnicode_Type" [clinic start generated code]*/ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ @@ -12903,7 +12903,7 @@ unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z); static PyObject * -unicode_maketrans(void *null, PyObject *args) +unicode_maketrans(PyUnicodeObject *null, PyObject *args) { PyObject *return_value = NULL; PyObject *x; @@ -12922,7 +12922,7 @@ static PyObject * unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z) -/*[clinic end generated code: checksum=90a3de8c494b304687e1e0d7e5fa8ba78eac6533]*/ +/*[clinic end generated code: checksum=5d41682159e78e404fd34489dc2ddea33045eaa7]*/ { PyObject *new = NULL, *key, *value; Py_ssize_t i = 0; diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -299,6 +299,10 @@ # Should be full lines with \n eol characters. self.initializers = [] + # The C statements needed to dynamically modify the values + # parsed by the parse call, before calling the impl. + self.modifications = [] + # The entries for the "keywords" array for PyArg_ParseTuple. # Should be individual strings representing the names. self.keywords = [] @@ -540,6 +544,7 @@ parser_definition_impl_call + {modifications} {return_value} = {c_basename}_impl({impl_arguments}); __________________________________________________ @@ -574,14 +579,14 @@ parser_definition_no_positional - if (!_PyArg_NoPositional("{name}", args)) + if ({self_type_check}!_PyArg_NoPositional("{name}", args)) goto exit; __________________________________________________ parser_definition_no_keywords - if (!_PyArg_NoKeywords("{name}", kwargs)) + if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) goto exit; __________________________________________________ @@ -690,7 +695,7 @@ impl_definition = templates['impl_definition'] impl_prototype = parser_prototype = parser_definition = None - parser_body_fields = None + parser_body_fields = () def parser_body(prototype, *fields): nonlocal parser_body_fields add, output = text_accumulator() @@ -1024,6 +1029,7 @@ template_dict['docstring'] = self.docstring_for_c_string(f) + template_dict['self_name'] = template_dict['self_type'] = template_dict['self_type_check'] = '' f_self.converter.set_template_dict(template_dict) f.return_converter.render(f, data) @@ -1031,6 +1037,7 @@ template_dict['declarations'] = "\n".join(data.declarations) template_dict['initializers'] = "\n\n".join(data.initializers) + template_dict['modifications'] = '\n\n'.join(data.modifications) template_dict['keywords'] = '"' + '", "'.join(data.keywords) + '"' template_dict['format_units'] = ''.join(data.format_units) template_dict['parse_arguments'] = ', '.join(data.parse_arguments) @@ -1059,6 +1066,7 @@ declarations=template_dict['declarations'], return_conversion=template_dict['return_conversion'], initializers=template_dict['initializers'], + modifications=template_dict['modifications'], cleanup=template_dict['cleanup'], ) @@ -1475,7 +1483,7 @@ self.add_destination("buffer", "buffer") self.add_destination("two-pass", "two-pass") if filename: - self.add_destination("file", "file", "{basename}.clinic{extension}") + self.add_destination("file", "file", "clinic/{filename}.h") d = self.destinations.get self.field_destinations = collections.OrderedDict(( @@ -1571,6 +1579,12 @@ if destination.type == 'file': try: + dirname = os.path.basename(destination.filename) + try: + os.makedirs(dirname) + except FileExistsError: + if not os.path.isdir(dirname): + fail("Can't write to destination " + repr(destination.filename) + ", can't make directory " + repr(dirname)) with open(destination.filename, "rt") as f: parser_2 = BlockParser(f.read(), language=self.language) blocks = list(parser_2) @@ -1695,10 +1709,12 @@ return "" class Class: - def __init__(self, name, module=None, cls=None): + def __init__(self, name, module=None, cls=None, typedef=None, type_object=None): self.name = name self.module = module self.cls = cls + self.typedef = typedef + self.type_object = type_object self.parent = cls or module self.classes = collections.OrderedDict() @@ -1979,6 +1995,7 @@ # Should we show this parameter in the generated # __text_signature__? This is *almost* always True. + # (It's only False for __new__, __init__, and METH_STATIC functions.) show_in_signature = True # Overrides the name used in a text signature. @@ -2049,6 +2066,11 @@ if initializers: data.initializers.append('/* initializers for ' + name + ' */\n' + initializers.rstrip()) + # modifications + modifications = self.modify() + if modifications: + data.modifications.append('/* modifications for ' + name + ' */\n' + modifications.rstrip()) + # keywords data.keywords.append(original_name) @@ -2151,6 +2173,14 @@ """ return "" + def modify(self): + """ + The C statements required to modify this variable after parsing. + Returns a string containing this code indented at column 0. + If no initialization is necessary, returns an empty string. + """ + return "" + def cleanup(self): """ The C statements required to clean up after this variable. @@ -2553,6 +2583,7 @@ def set_template_dict(self, template_dict): template_dict['self_name'] = self.name template_dict['self_type'] = self.parser_type + template_dict['self_type_check'] = '({self_name} == {self_type_object}) &&\n ' @@ -2807,6 +2838,7 @@ self.keyword_only = False self.group = 0 self.parameter_state = self.ps_start + self.seen_positional_with_default = False self.indent = IndentStack() self.kind = CALLABLE self.coexist = False @@ -2824,11 +2856,15 @@ module, cls = self.clinic._module_and_class(fields) if cls: fail("Can't nest a module inside a class!") + + if name in module.classes: + fail("Already defined module " + repr(name) + "!") + m = Module(name, module) module.modules[name] = m self.block.signatures.append(m) - def directive_class(self, name): + def directive_class(self, name, typedef, type_object): fields = name.split('.') in_classes = False parent = self @@ -2836,11 +2872,12 @@ so_far = [] module, cls = self.clinic._module_and_class(fields) - c = Class(name, module, cls) - if cls: - cls.classes[name] = c - else: - module.classes[name] = c + parent = cls or module + if name in parent.classes: + fail("Already defined class " + repr(name) + "!") + + c = Class(name, module, cls, typedef, type_object) + parent.classes[name] = c self.block.signatures.append(c) def directive_set(self, name, value): @@ -3034,6 +3071,8 @@ else: existing_function = None if not existing_function: + print("class", cls, "module", module, "exsiting", existing) + print("cls. functions", cls.functions) fail("Couldn't find existing function " + repr(existing) + "!") fields = [x.strip() for x in full_name.split('.')] @@ -3113,7 +3152,10 @@ # insert a self converter automatically _, name = correct_name_for_self(self.function) - sc = self.function.self_converter = self_converter(name, self.function) + kwargs = {} + if cls: + kwargs['type'] = cls.typedef + sc = self.function.self_converter = self_converter(name, self.function, **kwargs) p_self = Parameter(sc.name, inspect.Parameter.POSITIONAL_ONLY, function=self.function, converter=sc) self.function.parameters[sc.name] = p_self @@ -3174,18 +3216,21 @@ # "parameter_state". (Previously the code was a miasma of ifs and # separate boolean state variables.) The states are: # - # [ [ a, b, ] c, ] d, e, f, [ g, h, [ i ] ] / <- line - # 01 2 3 4 5 6 <- state transitions + # [ [ a, b, ] c, ] d, e, f=3, [ g, h, [ i ] ] / <- line + # 01 2 3 4 5 6 7 <- state transitions # # 0: ps_start. before we've seen anything. legal transitions are to 1 or 3. # 1: ps_left_square_before. left square brackets before required parameters. # 2: ps_group_before. in a group, before required parameters. - # 3: ps_required. required parameters. (renumber left groups!) - # 4: ps_group_after. in a group, after required parameters. - # 5: ps_right_square_after. right square brackets after required parameters. - # 6: ps_seen_slash. seen slash. + # 3: ps_required. required parameters, positional-or-keyword or positional-only + # (we don't know yet). (renumber left groups!) + # 4: ps_optional. positional-or-keyword or positional-only parameters that + # now must have default values. + # 5: ps_group_after. in a group, after required parameters. + # 6: ps_right_square_after. right square brackets after required parameters. + # 7: ps_seen_slash. seen slash. ps_start, ps_left_square_before, ps_group_before, ps_required, \ - ps_group_after, ps_right_square_after, ps_seen_slash = range(7) + ps_optional, ps_group_after, ps_right_square_after, ps_seen_slash = range(8) def state_parameters_start(self, line): if self.ignore_line(line): @@ -3244,21 +3289,25 @@ elif self.parameter_state == self.ps_group_before: if not self.group: self.to_required() - elif self.parameter_state == self.ps_group_after: + elif self.parameter_state in (self.ps_group_after, self.ps_optional): pass else: - fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")") + fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".a)") base, equals, default = line.rpartition('=') if not equals: base = default default = None + module = None try: ast_input = "def x({}): pass".format(base) module = ast.parse(ast_input) except SyntaxError: try: + # the last = was probably inside a function call, like + # i: int(nullable=True) + # so assume there was no actual default value. default = None ast_input = "def x({}): pass".format(line) module = ast.parse(ast_input) @@ -3274,16 +3323,20 @@ name, legacy, kwargs = self.parse_converter(parameter.annotation) if not default: + if self.parameter_state == self.ps_optional: + fail("Can't have a parameter without a default (" + repr(parameter_name) + ")\nafter a parameter with a default!") value = unspecified if 'py_default' in kwargs: fail("You can't specify py_default without specifying a default value!") else: + if self.parameter_state == self.ps_required: + self.parameter_state = self.ps_optional default = default.strip() ast_input = "x = {}".format(default) + bad = False try: module = ast.parse(ast_input) - bad = False if 'c_default' not in kwargs: # we can only represent very simple data values in C. # detect whether default is okay, via a blacklist @@ -3316,9 +3369,19 @@ blacklist.visit(module) bad = blacklist.bad else: - # if they specify a c_default, we can be more lenient about the default value. - # but at least ensure that we can turn it into text and reconstitute it correctly. - bad = default != repr(eval(default)) + # if they specify a c_default, we're way more lenient about + # the default value. as long as the string evals, and it's + # not "unspecified", we'll assume it's fine. + try: + value = eval(default) + if value == unspecified: + fail("'unspecified' is not a legal default value!") + except NameError: + pass # probably an unrecognized name constant, that's okay + except Exception as e: + fail("Malformed expression given as default value\n" + "{!r} raised {!r}".format(default, e)) + if bad: fail("Unsupported expression as default value: " + repr(default)) @@ -3432,7 +3495,7 @@ elif self.parameter_state in (self.ps_required, self.ps_group_after): self.parameter_state = self.ps_group_after else: - fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")") + fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".b)") self.group += 1 elif symbol == ']': if not self.group: @@ -3445,12 +3508,12 @@ elif self.parameter_state in (self.ps_group_after, self.ps_right_square_after): self.parameter_state = self.ps_right_square_after else: - fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")") + fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".c)") elif symbol == '/': - # ps_required is allowed here, that allows positional-only without option groups + # ps_required and ps_optional are allowed here, that allows positional-only without option groups # to work (and have default values!) - if (self.parameter_state not in (self.ps_required, self.ps_right_square_after, self.ps_group_before)) or self.group: - fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")") + if (self.parameter_state not in (self.ps_required, self.ps_optional, self.ps_right_square_after, self.ps_group_before)) or self.group: + fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".d)") if self.keyword_only: fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.") self.parameter_state = self.ps_seen_slash