Show More
@@ -14,6 +14,8 b'' | |||
|
14 | 14 | |
|
15 | 15 | #include "util.h" |
|
16 | 16 | |
|
17 | static char *versionerrortext = "Python minor version mismatch"; | |
|
18 | ||
|
17 | 19 | static int8_t hextable[256] = { |
|
18 | 20 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|
19 | 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 | 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 | 1926 | dirs_module_init(mod); |
|
1915 | 1927 | |
|
1916 | 1928 | indexType.tp_new = PyType_GenericNew; |
@@ -1928,6 +1940,24 b' static void module_init(PyObject *mod)' | |||
|
1928 | 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 | 1961 | #ifdef IS_PY3K |
|
1932 | 1962 | static struct PyModuleDef parsers_module = { |
|
1933 | 1963 | PyModuleDef_HEAD_INIT, |
@@ -1939,6 +1969,8 b' static struct PyModuleDef parsers_module' | |||
|
1939 | 1969 | |
|
1940 | 1970 | PyMODINIT_FUNC PyInit_parsers(void) |
|
1941 | 1971 | { |
|
1972 | if (check_python_version() == -1) | |
|
1973 | return; | |
|
1942 | 1974 | PyObject *mod = PyModule_Create(&parsers_module); |
|
1943 | 1975 | module_init(mod); |
|
1944 | 1976 | return mod; |
@@ -1946,6 +1978,8 b' PyMODINIT_FUNC PyInit_parsers(void)' | |||
|
1946 | 1978 | #else |
|
1947 | 1979 | PyMODINIT_FUNC initparsers(void) |
|
1948 | 1980 | { |
|
1981 | if (check_python_version() == -1) | |
|
1982 | return; | |
|
1949 | 1983 | PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc); |
|
1950 | 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 | 6 | from mercurial import parsers |
|
4 | 7 | from mercurial.node import nullid, nullrev |
|
5 | 8 | import struct |
|
9 | import subprocess | |
|
10 | import sys | |
|
6 | 11 | |
|
7 | 12 | # original python implementation |
|
8 | 13 | def gettype(q): |
@@ -95,7 +100,70 b' def parse_index2(data, inline):' | |||
|
95 | 100 | index, chunkcache = parsers.parse_index2(data, inline) |
|
96 | 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 | 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 | 167 | # Check that parse_index2() raises TypeError on bad arguments. |
|
100 | 168 | try: |
|
101 | 169 | parse_index2(0, True) |
General Comments 0
You need to be logged in to leave comments.
Login now