Mercurial > hgrepos > Python > libs > ConfigMix
comparison configmix/_speedups.c @ 554:36d7aa000435
Implement a C-version of Configuration.interpolate_variables
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 03 Jan 2022 00:11:41 +0100 |
| parents | 9d2bd411f5c5 |
| children | b7434a34a1f4 |
comparison
equal
deleted
inserted
replaced
| 553:9d2bd411f5c5 | 554:36d7aa000435 |
|---|---|
| 21 struct speedups_state { | 21 struct speedups_state { |
| 22 PyObject *DOT; | 22 PyObject *DOT; |
| 23 PyObject *QUOTE; | 23 PyObject *QUOTE; |
| 24 PyObject *NS_SEPARATOR; | 24 PyObject *NS_SEPARATOR; |
| 25 PyObject *FILTER_SEPARATOR; | 25 PyObject *FILTER_SEPARATOR; |
| 26 PyObject *EMPTY_FILTER; | |
| 27 PyObject *NONE_FILTER; | |
| 26 PyObject *EMPTY_STR; | 28 PyObject *EMPTY_STR; |
| 27 PyObject *QUOTE_MAP; | 29 PyObject *QUOTE_MAP; |
| 30 PyObject *MISSING; | |
| 31 PyObject *STARTTOK; | |
| 32 PyObject *ENDTOK; | |
| 28 }; | 33 }; |
| 29 | 34 |
| 30 | 35 |
| 31 static | 36 static |
| 32 int | 37 int |
| 682 PyTuple_SetItem(res, 1, o1); /* steals */ | 687 PyTuple_SetItem(res, 1, o1); /* steals */ |
| 683 return res; | 688 return res; |
| 684 } | 689 } |
| 685 | 690 |
| 686 | 691 |
| 692 static | |
| 693 PyObject * | |
| 694 fast_interpolate_variables(PyObject *self, PyObject *args) | |
| 695 { | |
| 696 PyObject *config; | |
| 697 PyObject *s; | |
| 698 PyObject *cache; | |
| 699 | |
| 700 Py_ssize_t s_len; | |
| 701 Py_ssize_t idx; | |
| 702 Py_ssize_t i, j; | |
| 703 PyObject *parts = NULL; | |
| 704 Py_ssize_t parts_len; | |
| 705 PyObject *res_parts = NULL; | |
| 706 PyObject *res = NULL; | |
| 707 PyObject *tmp; | |
| 708 PyObject *tmp2; | |
| 709 PyObject *pb; | |
| 710 Py_ssize_t pb_len; | |
| 711 PyObject *varname = NULL; | |
| 712 PyObject *varvalue = NULL; | |
| 713 PyObject *filters = NULL; | |
| 714 int cacheable; | |
| 715 int use_cache = 1; | |
| 716 int first_part_is_empty; | |
| 717 PyObject *err_type; | |
| 718 PyObject *err_value; | |
| 719 PyObject *err_tb; | |
| 720 struct speedups_state *sstate; | |
| 721 | |
| 722 if (!PyArg_UnpackTuple(args, "s", 3, 3, &config, &s, &cache)) { | |
| 723 return NULL; | |
| 724 } | |
| 725 s_len = PyUnicode_GetLength(s); /* also an implicit type check */ | |
| 726 if (s_len < 0) { | |
| 727 return NULL; | |
| 728 } | |
| 729 if (s_len < 4) { | |
| 730 PyErr_Clear(); | |
| 731 Py_INCREF(s); | |
| 732 return s; | |
| 733 } | |
| 734 sstate = PyModule_GetState(self); | |
| 735 if (sstate == NULL) { | |
| 736 PyErr_SetString(PyExc_RuntimeError, "no module state available"); | |
| 737 return NULL; | |
| 738 } | |
| 739 | |
| 740 idx = PyUnicode_Find(s, sstate->STARTTOK, 0, s_len, 1); | |
| 741 if (idx < 0) { | |
| 742 PyErr_Clear(); | |
| 743 Py_INCREF(s); | |
| 744 return s; | |
| 745 } | |
| 746 | |
| 747 res = PyDict_GetItem(cache, s); /* borrowed */ | |
| 748 if (res != NULL) { | |
| 749 if (res == sstate->MISSING) { | |
| 750 return PyErr_Format( | |
| 751 PyExc_KeyError, | |
| 752 "Cannot interpolate variables in string %R (cached)", | |
| 753 s); | |
| 754 } | |
| 755 else { | |
| 756 Py_INCREF(res); | |
| 757 return res; | |
| 758 } | |
| 759 } | |
| 760 | |
| 761 parts = PyUnicode_Split(s, sstate->STARTTOK, -1); | |
| 762 if (parts == NULL) { | |
| 763 goto error; | |
| 764 } | |
| 765 parts_len = PyList_Size(parts); | |
| 766 if (parts_len < 0) { | |
| 767 goto error; | |
| 768 } | |
| 769 res_parts = PyList_New(1); | |
| 770 if (res_parts == NULL) { | |
| 771 goto error; | |
| 772 } | |
| 773 | |
| 774 tmp = PyList_GetItem(parts, 0); /* borrowed */ | |
| 775 if (tmp == NULL) { | |
| 776 goto error; | |
| 777 } | |
| 778 /* | |
| 779 * The first item may be also the empty string if `s' starts with | |
| 780 * an interpolation token. | |
| 781 */ | |
| 782 first_part_is_empty = PyObject_Not(tmp); | |
| 783 Py_INCREF(tmp); /* because PyList_SetItem steals -- and o is borrowed */ | |
| 784 PyList_SetItem(res_parts, 0, tmp); /* steals -- cannot fail */ | |
| 785 tmp = NULL; | |
| 786 | |
| 787 for (i=1; i<parts_len; i++) { | |
| 788 pb = PyList_GetItem(parts, i); /* borrowed */ | |
| 789 pb_len = PyUnicode_GetLength(pb); | |
| 790 if (pb_len < 0) { | |
| 791 goto error; | |
| 792 } | |
| 793 idx = PyUnicode_Find(pb, sstate->ENDTOK, 0, pb_len, 1); | |
| 794 if (idx < 0) { | |
| 795 /* | |
| 796 * Behave similar to the pure-Python version: copy the complete | |
| 797 * rest as-is. Also include the start tokens! | |
| 798 */ | |
| 799 if (PyList_Append(res_parts, sstate->STARTTOK) < 0) { | |
| 800 goto error; | |
| 801 } | |
| 802 if (PyList_Append(res_parts, pb) < 0) { | |
| 803 goto error; | |
| 804 } | |
| 805 for (j=i+1; j<parts_len; j++) { | |
| 806 if (PyList_Append(res_parts, sstate->STARTTOK) < 0) { | |
| 807 goto error; | |
| 808 } | |
| 809 pb = PyList_GetItem(parts, j); /* borrowed */ | |
| 810 if (PyList_Append(res_parts, pb) < 0) { | |
| 811 goto error; | |
| 812 } | |
| 813 } | |
| 814 break; /* the for-loop */ | |
| 815 } | |
| 816 | |
| 817 varname = PyUnicode_Substring(pb, 0, idx); | |
| 818 if (varname == NULL) { | |
| 819 goto error; | |
| 820 } | |
| 821 | |
| 822 tmp = _fast_split_filters(varname, NULL, sstate); | |
| 823 if (tmp == NULL) { | |
| 824 goto error; | |
| 825 } | |
| 826 if (PyTuple_Size(tmp) != 2) { | |
| 827 PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected"); | |
| 828 Py_DECREF(tmp); | |
| 829 goto error; | |
| 830 } | |
| 831 /* Unpack the result tuple */ | |
| 832 tmp2 = PyTuple_GetItem(tmp, 0); /* borrowed -- cannot fail */ | |
| 833 Py_DECREF(varname); | |
| 834 Py_INCREF(tmp2); | |
| 835 varname = tmp2; | |
| 836 tmp2 = PyTuple_GetItem(tmp, 1); /* borrowed -- cannot fail */ | |
| 837 Py_INCREF(tmp2); | |
| 838 filters = tmp2; | |
| 839 Py_DECREF(tmp); | |
| 840 tmp = tmp2 = NULL; | |
| 841 | |
| 842 tmp = PyObject_CallMethod( | |
| 843 config, "_getvar_s_with_cache_info", "O", varname); | |
| 844 if (tmp == NULL) { | |
| 845 if (PyErr_ExceptionMatches(PyExc_KeyError)) { | |
| 846 /* XXX TBD handle KeyError (None and Empty-filter) */ | |
| 847 cacheable = 1; | |
| 848 if (PySequence_Contains(filters, sstate->NONE_FILTER) == 1) { | |
| 849 PyErr_Clear(); | |
| 850 Py_INCREF(Py_None); | |
| 851 varvalue = Py_None; | |
| 852 } | |
| 853 else { | |
| 854 if (PySequence_Contains(filters, sstate->EMPTY_FILTER) == 1) { | |
| 855 PyErr_Clear(); | |
| 856 Py_INCREF(sstate->EMPTY_STR); | |
| 857 varvalue = sstate->EMPTY_STR; | |
| 858 } | |
| 859 else { | |
| 860 PyErr_Fetch(&err_type, &err_value, &err_tb); | |
| 861 /* this does NOT steal */ | |
| 862 PyDict_SetItem(cache, s, sstate->MISSING); | |
| 863 PyErr_Restore(err_type, err_value, err_tb); | |
| 864 goto error; | |
| 865 } | |
| 866 } | |
| 867 } | |
| 868 else { | |
| 869 /* other exception/error than KeyError */ | |
| 870 goto error; | |
| 871 } | |
| 872 } | |
| 873 else { | |
| 874 if (PyTuple_Size(tmp) != 2) { | |
| 875 Py_DECREF(tmp); | |
| 876 PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected"); | |
| 877 goto error; | |
| 878 } | |
| 879 /* unpack the result */ | |
| 880 varvalue = PyTuple_GetItem(tmp, 0); /* borrowed -- but want own */ | |
| 881 Py_INCREF(varvalue); | |
| 882 cacheable = PyObject_IsTrue(PyTuple_GetItem(tmp, 1)); | |
| 883 Py_DECREF(tmp); tmp = NULL; | |
| 884 } | |
| 885 | |
| 886 if (!cacheable) { | |
| 887 use_cache = 0; | |
| 888 } | |
| 889 | |
| 890 Py_DECREF(varname); varname = NULL; | |
| 891 | |
| 892 tmp = PyObject_CallMethod( | |
| 893 config, "_apply_filters", "OO", filters, varvalue); | |
| 894 if (tmp == NULL) { | |
| 895 goto error; | |
| 896 } | |
| 897 Py_DECREF(varvalue); | |
| 898 varvalue = tmp; | |
| 899 tmp = NULL; | |
| 900 | |
| 901 /* | |
| 902 * Dont apply and type conversions to the variable value if | |
| 903 * the whole `s` is just one expansion | |
| 904 */ | |
| 905 if (first_part_is_empty && (i == 1) && (pb_len == s_len - 2) && (idx == pb_len - 2)) { | |
| 906 res = varvalue; varvalue = NULL; | |
| 907 goto success; /* break out early */ | |
| 908 } | |
| 909 if (varvalue != Py_None) { | |
| 910 tmp = PyObject_Str(varvalue); | |
| 911 if (tmp == NULL) { | |
| 912 goto error; | |
| 913 } | |
| 914 if (PyList_Append(res_parts, tmp) < 0) { | |
| 915 Py_DECREF(tmp); | |
| 916 goto error; | |
| 917 } | |
| 918 Py_DECREF(tmp); | |
| 919 } | |
| 920 Py_DECREF(varvalue); varvalue = NULL; | |
| 921 /* append the rest of the string */ | |
| 922 tmp = PyUnicode_Substring(pb, idx+2, pb_len); | |
| 923 if (tmp == NULL) { | |
| 924 goto error; | |
| 925 } | |
| 926 if (PyList_Append(res_parts, tmp) < 0) { | |
| 927 Py_DECREF(tmp); | |
| 928 goto error; | |
| 929 } | |
| 930 Py_DECREF(tmp); tmp = NULL; | |
| 931 } | |
| 932 | |
| 933 res = PyUnicode_Join(sstate->EMPTY_STR, res_parts); | |
| 934 if (res == NULL) { | |
| 935 goto error; | |
| 936 } | |
| 937 | |
| 938 success: | |
| 939 Py_DECREF(parts); | |
| 940 Py_DECREF(res_parts); | |
| 941 Py_XDECREF(filters); | |
| 942 | |
| 943 if (use_cache) { | |
| 944 PyDict_SetItem(cache, s, res); | |
| 945 PyErr_Clear(); /* clear any possible cache-related error */ | |
| 946 } | |
| 947 return res; | |
| 948 | |
| 949 error: | |
| 950 Py_XDECREF(varname); | |
| 951 Py_XDECREF(varvalue); | |
| 952 Py_XDECREF(parts); | |
| 953 Py_XDECREF(res_parts); | |
| 954 Py_XDECREF(res); | |
| 955 Py_XDECREF(filters); | |
| 956 return NULL; | |
| 957 } | |
| 958 | |
| 959 | |
| 960 static | |
| 961 PyObject * | |
| 962 sync_MISSING(PyObject *self, PyObject *missing) | |
| 963 { | |
| 964 struct speedups_state *sstate; | |
| 965 | |
| 966 sstate = PyModule_GetState(self); | |
| 967 if (sstate == NULL) { | |
| 968 PyErr_SetString(PyExc_RuntimeError, "no module state available"); | |
| 969 return NULL; | |
| 970 } | |
| 971 if (sstate->MISSING != NULL) { | |
| 972 PyErr_SetString(PyExc_RuntimeError, "_MISSING already set"); | |
| 973 return NULL; | |
| 974 } | |
| 975 Py_INCREF(missing); | |
| 976 sstate->MISSING = missing; | |
| 977 Py_RETURN_NONE; | |
| 978 } | |
| 979 | |
| 980 | |
| 687 static struct PyMethodDef speedups_methods[] = { | 981 static struct PyMethodDef speedups_methods[] = { |
| 688 {"fast_unquote", fast_unquote, METH_O, PyDoc_STR("C-implementation of configmix.unquote")}, | 982 {"fast_unquote", fast_unquote, METH_O, PyDoc_STR("C-implementation of configmix.unquote")}, |
| 689 {"fast_quote", fast_quote, METH_O, PyDoc_STR("C-implementation of configmix.quote")}, | 983 {"fast_quote", fast_quote, METH_O, PyDoc_STR("C-implementation of configmix.quote")}, |
| 690 {"fast_pathstr2path", fast_pathstr2path, METH_O, PyDoc_STR("C-implementation of configmix.pathstr2path")}, | 984 {"fast_pathstr2path", fast_pathstr2path, METH_O, PyDoc_STR("C-implementation of configmix.pathstr2path")}, |
| 691 {"_fast_split_filters", fast_split_filters, METH_O, PyDoc_STR("C-implementation of configmix.config._split_filters")}, | 985 {"_fast_split_filters", fast_split_filters, METH_O, PyDoc_STR("C-implementation of configmix.config._split_filters")}, |
| 692 {"_fast_split_ns", fast_split_ns, METH_O, PyDoc_STR("C-implementation of configmix.config._split_ns")}, | 986 {"_fast_split_ns", fast_split_ns, METH_O, PyDoc_STR("C-implementation of configmix.config._split_ns")}, |
| 987 {"_fast_interpolate_variables", fast_interpolate_variables, METH_VARARGS, PyDoc_STR("C-implementation of configmix.config.Configuration.interpolate_variables")}, | |
| 988 {"_sync_MISSING", sync_MISSING, METH_O, PyDoc_STR("Internal function to easily sync the _MISSING object with configmix.config")}, | |
| 989 | |
| 693 {NULL, NULL, 0, NULL} | 990 {NULL, NULL, 0, NULL} |
| 694 }; | 991 }; |
| 695 | 992 |
| 696 #define STRINGIFY(s) #s | 993 #define STRINGIFY(s) #s |
| 697 #define XSTRINGIFY(s) STRINGIFY(s) | 994 #define XSTRINGIFY(s) STRINGIFY(s) |
| 736 sstate->FILTER_SEPARATOR = PyUnicode_FromStringAndSize("|", 1); | 1033 sstate->FILTER_SEPARATOR = PyUnicode_FromStringAndSize("|", 1); |
| 737 if (sstate->FILTER_SEPARATOR == NULL) { | 1034 if (sstate->FILTER_SEPARATOR == NULL) { |
| 738 return -1; | 1035 return -1; |
| 739 } | 1036 } |
| 740 PyUnicode_InternInPlace(&(sstate->FILTER_SEPARATOR)); | 1037 PyUnicode_InternInPlace(&(sstate->FILTER_SEPARATOR)); |
| 1038 | |
| 1039 sstate->EMPTY_FILTER = PyUnicode_FromStringAndSize("Empty", 5); | |
| 1040 if (sstate->EMPTY_FILTER == NULL) { | |
| 1041 return -1; | |
| 1042 } | |
| 1043 PyUnicode_InternInPlace(&(sstate->EMPTY_FILTER)); | |
| 1044 | |
| 1045 sstate->NONE_FILTER = PyUnicode_FromStringAndSize("None", 4); | |
| 1046 if (sstate->NONE_FILTER == NULL) { | |
| 1047 return -1; | |
| 1048 } | |
| 1049 PyUnicode_InternInPlace(&(sstate->NONE_FILTER)); | |
| 741 | 1050 |
| 742 sstate->EMPTY_STR = PyUnicode_FromStringAndSize("", 0); | 1051 sstate->EMPTY_STR = PyUnicode_FromStringAndSize("", 0); |
| 743 if (sstate->EMPTY_STR == NULL) { | 1052 if (sstate->EMPTY_STR == NULL) { |
| 744 return -1; | 1053 return -1; |
| 745 } | 1054 } |
| 760 0x5d, "%x5d"); | 1069 0x5d, "%x5d"); |
| 761 if (sstate->QUOTE_MAP == NULL) { | 1070 if (sstate->QUOTE_MAP == NULL) { |
| 762 return -1; | 1071 return -1; |
| 763 } | 1072 } |
| 764 | 1073 |
| 1074 sstate->STARTTOK = PyUnicode_FromStringAndSize("{{", 2); | |
| 1075 if (sstate->STARTTOK == NULL) { | |
| 1076 return -1; | |
| 1077 } | |
| 1078 PyUnicode_InternInPlace(&(sstate->STARTTOK)); | |
| 1079 | |
| 1080 sstate->ENDTOK = PyUnicode_FromStringAndSize("}}", 2); | |
| 1081 if (sstate->ENDTOK == NULL) { | |
| 1082 return -1; | |
| 1083 } | |
| 1084 PyUnicode_InternInPlace(&(sstate->ENDTOK)); | |
| 1085 | |
| 765 return 0; | 1086 return 0; |
| 766 } | 1087 } |
| 767 | 1088 |
| 768 | 1089 |
| 769 static | 1090 static |
| 775 if (sstate != NULL) { | 1096 if (sstate != NULL) { |
| 776 Py_VISIT(sstate->DOT); | 1097 Py_VISIT(sstate->DOT); |
| 777 Py_VISIT(sstate->QUOTE); | 1098 Py_VISIT(sstate->QUOTE); |
| 778 Py_VISIT(sstate->NS_SEPARATOR); | 1099 Py_VISIT(sstate->NS_SEPARATOR); |
| 779 Py_VISIT(sstate->FILTER_SEPARATOR); | 1100 Py_VISIT(sstate->FILTER_SEPARATOR); |
| 1101 Py_VISIT(sstate->EMPTY_FILTER); | |
| 1102 Py_VISIT(sstate->NONE_FILTER); | |
| 780 Py_VISIT(sstate->EMPTY_STR); | 1103 Py_VISIT(sstate->EMPTY_STR); |
| 781 Py_VISIT(sstate->QUOTE_MAP); | 1104 Py_VISIT(sstate->QUOTE_MAP); |
| 1105 Py_VISIT(sstate->MISSING); | |
| 1106 Py_VISIT(sstate->STARTTOK); | |
| 1107 Py_VISIT(sstate->ENDTOK); | |
| 782 } | 1108 } |
| 783 return 0; | 1109 return 0; |
| 784 } | 1110 } |
| 785 | 1111 |
| 786 | 1112 |
| 793 if (sstate != NULL) { | 1119 if (sstate != NULL) { |
| 794 Py_CLEAR(sstate->DOT); | 1120 Py_CLEAR(sstate->DOT); |
| 795 Py_CLEAR(sstate->QUOTE); | 1121 Py_CLEAR(sstate->QUOTE); |
| 796 Py_CLEAR(sstate->NS_SEPARATOR); | 1122 Py_CLEAR(sstate->NS_SEPARATOR); |
| 797 Py_CLEAR(sstate->FILTER_SEPARATOR); | 1123 Py_CLEAR(sstate->FILTER_SEPARATOR); |
| 1124 Py_CLEAR(sstate->EMPTY_FILTER); | |
| 1125 Py_CLEAR(sstate->NONE_FILTER); | |
| 798 Py_CLEAR(sstate->EMPTY_STR); | 1126 Py_CLEAR(sstate->EMPTY_STR); |
| 799 Py_CLEAR(sstate->QUOTE_MAP); | 1127 Py_CLEAR(sstate->QUOTE_MAP); |
| 1128 Py_CLEAR(sstate->MISSING); | |
| 1129 Py_CLEAR(sstate->STARTTOK); | |
| 1130 Py_CLEAR(sstate->ENDTOK); | |
| 800 } | 1131 } |
| 801 return 0; | 1132 return 0; |
| 802 } | 1133 } |
| 803 | 1134 |
| 804 | 1135 |
