Show More
@@ -14,6 +14,8 b'' | |||||
14 |
|
14 | |||
15 | #include "util.h" |
|
15 | #include "util.h" | |
16 |
|
16 | |||
|
17 | static char *versionerrortext = "Python minor version mismatch"; | |||
|
18 | ||||
17 | static int8_t hextable[256] = { |
|
19 | static int8_t hextable[256] = { | |
18 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|
20 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
19 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|
21 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
@@ -1911,6 +1913,16 b' void dirs_module_init(PyObject *mod);' | |||||
1911 |
|
1913 | |||
1912 | static void module_init(PyObject *mod) |
|
1914 | static void module_init(PyObject *mod) | |
1913 | { |
|
1915 | { | |
|
1916 | /* This module constant has two purposes. First, it lets us unit test | |||
|
1917 | * the ImportError raised without hard-coding any error text. This | |||
|
1918 | * means we can change the text in the future without breaking tests, | |||
|
1919 | * even across changesets without a recompile. Second, its presence | |||
|
1920 | * can be used to determine whether the version-checking logic is | |||
|
1921 | * present, which also helps in testing across changesets without a | |||
|
1922 | * recompile. Note that this means the pure-Python version of parsers | |||
|
1923 | * should not have this module constant. */ | |||
|
1924 | PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext); | |||
|
1925 | ||||
1914 | dirs_module_init(mod); |
|
1926 | dirs_module_init(mod); | |
1915 |
|
1927 | |||
1916 | indexType.tp_new = PyType_GenericNew; |
|
1928 | indexType.tp_new = PyType_GenericNew; | |
@@ -1928,6 +1940,24 b' static void module_init(PyObject *mod)' | |||||
1928 | dirstate_unset = Py_BuildValue("ciii", 'n', 0, -1, -1); |
|
1940 | dirstate_unset = Py_BuildValue("ciii", 'n', 0, -1, -1); | |
1929 | } |
|
1941 | } | |
1930 |
|
1942 | |||
|
1943 | static int check_python_version(void) | |||
|
1944 | { | |||
|
1945 | PyObject *sys = PyImport_ImportModule("sys"); | |||
|
1946 | long hexversion = PyInt_AsLong(PyObject_GetAttrString(sys, "hexversion")); | |||
|
1947 | /* sys.hexversion is a 32-bit number by default, so the -1 case | |||
|
1948 | * should only occur in unusual circumstances (e.g. if sys.hexversion | |||
|
1949 | * is manually set to an invalid value). */ | |||
|
1950 | if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) { | |||
|
1951 | PyErr_Format(PyExc_ImportError, "%s: The Mercurial extension " | |||
|
1952 | "modules were compiled with Python " PY_VERSION ", but " | |||
|
1953 | "Mercurial is currently using Python with sys.hexversion=%ld: " | |||
|
1954 | "Python %s\n at: %s", versionerrortext, hexversion, | |||
|
1955 | Py_GetVersion(), Py_GetProgramFullPath()); | |||
|
1956 | return -1; | |||
|
1957 | } | |||
|
1958 | return 0; | |||
|
1959 | } | |||
|
1960 | ||||
1931 | #ifdef IS_PY3K |
|
1961 | #ifdef IS_PY3K | |
1932 | static struct PyModuleDef parsers_module = { |
|
1962 | static struct PyModuleDef parsers_module = { | |
1933 | PyModuleDef_HEAD_INIT, |
|
1963 | PyModuleDef_HEAD_INIT, | |
@@ -1939,6 +1969,8 b' static struct PyModuleDef parsers_module' | |||||
1939 |
|
1969 | |||
1940 | PyMODINIT_FUNC PyInit_parsers(void) |
|
1970 | PyMODINIT_FUNC PyInit_parsers(void) | |
1941 | { |
|
1971 | { | |
|
1972 | if (check_python_version() == -1) | |||
|
1973 | return; | |||
1942 | PyObject *mod = PyModule_Create(&parsers_module); |
|
1974 | PyObject *mod = PyModule_Create(&parsers_module); | |
1943 | module_init(mod); |
|
1975 | module_init(mod); | |
1944 | return mod; |
|
1976 | return mod; | |
@@ -1946,6 +1978,8 b' PyMODINIT_FUNC PyInit_parsers(void)' | |||||
1946 | #else |
|
1978 | #else | |
1947 | PyMODINIT_FUNC initparsers(void) |
|
1979 | PyMODINIT_FUNC initparsers(void) | |
1948 | { |
|
1980 | { | |
|
1981 | if (check_python_version() == -1) | |||
|
1982 | return; | |||
1949 | PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc); |
|
1983 | PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc); | |
1950 | module_init(mod); |
|
1984 | module_init(mod); | |
1951 | } |
|
1985 | } |
@@ -1,8 +1,13 b'' | |||||
1 |
"""This unit test tests parsers.parse_index2(). |
|
1 | """This unit test primarily tests parsers.parse_index2(). | |
|
2 | ||||
|
3 | It also checks certain aspects of the parsers module as a whole. | |||
|
4 | """ | |||
2 |
|
5 | |||
3 | from mercurial import parsers |
|
6 | from mercurial import parsers | |
4 | from mercurial.node import nullid, nullrev |
|
7 | from mercurial.node import nullid, nullrev | |
5 | import struct |
|
8 | import struct | |
|
9 | import subprocess | |||
|
10 | import sys | |||
6 |
|
11 | |||
7 | # original python implementation |
|
12 | # original python implementation | |
8 | def gettype(q): |
|
13 | def gettype(q): | |
@@ -95,7 +100,70 b' def parse_index2(data, inline):' | |||||
95 | index, chunkcache = parsers.parse_index2(data, inline) |
|
100 | index, chunkcache = parsers.parse_index2(data, inline) | |
96 | return list(index), chunkcache |
|
101 | return list(index), chunkcache | |
97 |
|
102 | |||
|
103 | def importparsers(hexversion): | |||
|
104 | """Import mercurial.parsers with the given sys.hexversion.""" | |||
|
105 | # The file parsers.c inspects sys.hexversion to determine the version | |||
|
106 | # of the currently-running Python interpreter, so we monkey-patch | |||
|
107 | # sys.hexversion to simulate using different versions. | |||
|
108 | code = ("import sys; sys.hexversion=%s; " | |||
|
109 | "import mercurial.parsers" % hexversion) | |||
|
110 | cmd = "python -c \"%s\"" % code | |||
|
111 | # We need to do these tests inside a subprocess because parser.c's | |||
|
112 | # version-checking code happens inside the module init function, and | |||
|
113 | # when using reload() to reimport an extension module, "The init function | |||
|
114 | # of extension modules is not called a second time" | |||
|
115 | # (from http://docs.python.org/2/library/functions.html?#reload). | |||
|
116 | p = subprocess.Popen(cmd, shell=True, | |||
|
117 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |||
|
118 | return p.communicate() # returns stdout, stderr | |||
|
119 | ||||
|
120 | def printhexfail(testnumber, hexversion, stdout, expected): | |||
|
121 | try: | |||
|
122 | hexstring = hex(hexversion) | |||
|
123 | except TypeError: | |||
|
124 | hexstring = None | |||
|
125 | print ("FAILED: version test #%s with Python %s and patched " | |||
|
126 | "sys.hexversion %r (%r):\n Expected %s but got:\n-->'%s'\n" % | |||
|
127 | (testnumber, sys.version_info, hexversion, hexstring, expected, | |||
|
128 | stdout)) | |||
|
129 | ||||
|
130 | def testversionokay(testnumber, hexversion): | |||
|
131 | stdout, stderr = importparsers(hexversion) | |||
|
132 | if stdout: | |||
|
133 | printhexfail(testnumber, hexversion, stdout, expected="no stdout") | |||
|
134 | ||||
|
135 | def testversionfail(testnumber, hexversion): | |||
|
136 | stdout, stderr = importparsers(hexversion) | |||
|
137 | # We include versionerrortext to distinguish from other ImportErrors. | |||
|
138 | errtext = "ImportError: %s" % parsers.versionerrortext | |||
|
139 | if errtext not in stdout: | |||
|
140 | printhexfail(testnumber, hexversion, stdout, | |||
|
141 | expected="stdout to contain %r" % errtext) | |||
|
142 | ||||
|
143 | def makehex(major, minor, micro): | |||
|
144 | return int("%x%02x%02x00" % (major, minor, micro), 16) | |||
|
145 | ||||
|
146 | def runversiontests(): | |||
|
147 | """Check the version-detection logic when importing parsers.""" | |||
|
148 | info = sys.version_info | |||
|
149 | major, minor, micro = info[0], info[1], info[2] | |||
|
150 | # Test same major-minor versions. | |||
|
151 | testversionokay(1, makehex(major, minor, micro)) | |||
|
152 | testversionokay(2, makehex(major, minor, micro + 1)) | |||
|
153 | # Test different major-minor versions. | |||
|
154 | testversionfail(3, makehex(major + 1, minor, micro)) | |||
|
155 | testversionfail(4, makehex(major, minor + 1, micro)) | |||
|
156 | testversionfail(5, "'foo'") | |||
|
157 | ||||
98 | def runtest() : |
|
158 | def runtest() : | |
|
159 | # Only test the version-detection logic if it is present. | |||
|
160 | try: | |||
|
161 | parsers.versionerrortext | |||
|
162 | except AttributeError: | |||
|
163 | pass | |||
|
164 | else: | |||
|
165 | runversiontests() | |||
|
166 | ||||
99 | # Check that parse_index2() raises TypeError on bad arguments. |
|
167 | # Check that parse_index2() raises TypeError on bad arguments. | |
100 | try: |
|
168 | try: | |
101 | parse_index2(0, True) |
|
169 | parse_index2(0, True) |
General Comments 0
You need to be logged in to leave comments.
Login now