cpython: 8dbf8edb7128 (original) (raw)
--- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -1,3 +1,4 @@ +import io import sys import os import marshal @@ -55,6 +56,27 @@ TESTPACK2 = "ziptestpackage2" TEMP_ZIP = os.path.abspath("junk95142" + os.extsep + "zip") +def _write_zip_package(zipname, files,
data_to_prepend=b"", compression=ZIP_STORED):[](#l1.13)
- z = ZipFile(zipname, "w")
- try:
for name, (mtime, data) in files.items():[](#l1.16)
zinfo = ZipInfo(name, time.localtime(mtime))[](#l1.17)
zinfo.compress_type = compression[](#l1.18)
z.writestr(zinfo, data)[](#l1.19)
- finally:
z.close()[](#l1.21)
- if data_to_prepend:
# Prepend data to the start of the zipfile[](#l1.24)
with open(zipname, "rb") as f:[](#l1.25)
zip_data = f.read()[](#l1.26)
with open(zipname, "wb") as f:[](#l1.28)
f.write(data_to_prepend)[](#l1.29)
f.write(zip_data)[](#l1.30)
+ + class UncompressedZipImportTestCase(ImportHooksBaseTestCase): compression = ZIP_STORED @@ -67,26 +89,9 @@ class UncompressedZipImportTestCase(Impo ImportHooksBaseTestCase.setUp(self) def doTest(self, expected_ext, files, *modules, **kw):
z = ZipFile(TEMP_ZIP, "w")[](#l1.40)
_write_zip_package(TEMP_ZIP, files, data_to_prepend=kw.get("stuff"),[](#l1.41)
compression=self.compression)[](#l1.42) try:[](#l1.43)
for name, (mtime, data) in files.items():[](#l1.44)
zinfo = ZipInfo(name, time.localtime(mtime))[](#l1.45)
zinfo.compress_type = self.compression[](#l1.46)
z.writestr(zinfo, data)[](#l1.47)
z.close()[](#l1.48)
stuff = kw.get("stuff", None)[](#l1.50)
if stuff is not None:[](#l1.51)
# Prepend 'stuff' to the start of the zipfile[](#l1.52)
f = open(TEMP_ZIP, "rb")[](#l1.53)
data = f.read()[](#l1.54)
f.close()[](#l1.55)
f = open(TEMP_ZIP, "wb")[](#l1.57)
f.write(stuff)[](#l1.58)
f.write(data)[](#l1.59)
f.close()[](#l1.60)
- sys.path.insert(0, TEMP_ZIP) mod = import(".".join(modules), globals(), locals(), @@ -101,7 +106,6 @@ class UncompressedZipImportTestCase(Impo self.assertEqual(file, os.path.join(TEMP_ZIP, *modules) + expected_ext) finally:
z.close()[](#l1.69) os.remove(TEMP_ZIP)[](#l1.70)
def testAFakeZlib(self): @@ -387,6 +391,64 @@ class CompressedZipImportTestCase(Uncomp compression = ZIP_DEFLATED +class ZipFileModifiedAfterImportTestCase(ImportHooksBaseTestCase):
- def setUp(self):
zipimport._zip_directory_cache.clear()[](#l1.79)
zipimport._zip_stat_cache.clear()[](#l1.80)
ImportHooksBaseTestCase.setUp(self)[](#l1.81)
- def tearDown(self):
ImportHooksBaseTestCase.tearDown(self)[](#l1.84)
if os.path.exists(TEMP_ZIP):[](#l1.85)
os.remove(TEMP_ZIP)[](#l1.86)
- def testZipFileChangesAfterFirstImport(self):
"""Alter the zip file after caching its index and try an import."""[](#l1.89)
packdir = TESTPACK + os.sep[](#l1.90)
files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),[](#l1.91)
packdir + TESTMOD + ".py": (NOW, "test_value = 38\n"),[](#l1.92)
"ziptest_a.py": (NOW, "test_value = 23\n"),[](#l1.93)
"ziptest_b.py": (NOW, "test_value = 42\n"),[](#l1.94)
"ziptest_c.py": (NOW, "test_value = 1337\n")}[](#l1.95)
zipfile_path = TEMP_ZIP[](#l1.96)
_write_zip_package(zipfile_path, files)[](#l1.97)
self.assertTrue(os.path.exists(zipfile_path))[](#l1.98)
sys.path.insert(0, zipfile_path)[](#l1.99)
# Import something out of the zipfile and confirm it is correct.[](#l1.101)
testmod = __import__(TESTPACK + "." + TESTMOD,[](#l1.102)
globals(), locals(), ["__dummy__"])[](#l1.103)
self.assertEqual(testmod.test_value, 38)[](#l1.104)
# Import something else out of the zipfile and confirm it is correct.[](#l1.105)
ziptest_b = __import__("ziptest_b", globals(), locals(), ["test_value"])[](#l1.106)
self.assertEqual(ziptest_b.test_value, 42)[](#l1.107)
# Truncate and fill the zip file with non-zip garbage.[](#l1.109)
with io.open(zipfile_path, "rb") as orig_zip_file:[](#l1.110)
orig_zip_file_contents = orig_zip_file.read()[](#l1.111)
with io.open(zipfile_path, "wb") as byebye_valid_zip_file:[](#l1.112)
byebye_valid_zip_file.write(b"Tear down this wall!\n"*1987)[](#l1.113)
# Now that the zipfile has been replaced, import something else from it[](#l1.114)
# which should fail as the file contents are now garbage.[](#l1.115)
with self.assertRaises(ImportError):[](#l1.116)
ziptest_a = __import__("ziptest_a", globals(), locals(),[](#l1.117)
["test_value"])[](#l1.118)
# Now lets make it a valid zipfile that has some garbage at the start.[](#l1.120)
# This alters all of the offsets within the file[](#l1.121)
with io.open(zipfile_path, "wb") as new_zip_file:[](#l1.122)
new_zip_file.write(b"X"*1991) # The year Python was created.[](#l1.123)
new_zip_file.write(orig_zip_file_contents)[](#l1.124)
# Now that the zip file has been "restored" to a valid but different[](#l1.126)
# zipfile the zipimporter should *successfully* re-read the new zip[](#l1.127)
# file's end of file central index and be able to import from it again.[](#l1.128)
ziptest_a = __import__("ziptest_a", globals(), locals(), ["test_value"])[](#l1.129)
self.assertEqual(ziptest_a.test_value, 23)[](#l1.130)
ziptest_c = __import__("ziptest_c", globals(), locals(), ["test_value"])[](#l1.131)
self.assertEqual(ziptest_c.test_value, 1337)[](#l1.132)
+ + class BadFileZipImportTestCase(unittest.TestCase): def assertZipFailure(self, filename): self.assertRaises(zipimport.ZipImportError, @@ -464,6 +526,7 @@ def test_main(): UncompressedZipImportTestCase, CompressedZipImportTestCase, BadFileZipImportTestCase,
--- a/Modules/zipimport.c +++ b/Modules/zipimport.c @@ -42,10 +42,16 @@ struct _zipimporter { static PyObject *ZipImportError; static PyObject *zip_directory_cache = NULL; +static PyObject zip_stat_cache = NULL; +/ posix.fstat or nt.fstat function. Used due to posixmodule.c's
- superior fstat implementation over libc's on Windows. */ +static PyObject fstat_function = NULL; / posix.fstat() or nt.fstat() / / forward decls */ -static PyObject *read_directory(char *archive); -static PyObject *get_data(char *archive, PyObject *toc_entry); +static FILE *fopen_rb_and_stat(char *path, PyObject **py_stat_p); +static FILE *safely_reopen_archive(ZipImporter *self, char **archive_p); +static PyObject *read_directory(FILE *fp, char *archive); +static PyObject *get_data(FILE *fp, char *archive, PyObject *toc_entry); static PyObject *get_module_code(ZipImporter *self, char *fullname, int *p_ispackage, char **p_modpath);
@@ -126,12 +132,38 @@ zipimporter_init(ZipImporter *self, PyOb PyObject *files; files = PyDict_GetItemString(zip_directory_cache, path); if (files == NULL) {
files = read_directory(buf);[](#l2.26)
if (files == NULL)[](#l2.27)
PyObject *zip_stat = NULL;[](#l2.28)
FILE *fp = fopen_rb_and_stat(buf, &zip_stat);[](#l2.29)
if (fp == NULL) {[](#l2.30)
PyErr_Format(ZipImportError, "can't open Zip file: "[](#l2.31)
"'%.200s'", buf);[](#l2.32)
Py_XDECREF(zip_stat);[](#l2.33) return -1;[](#l2.34)
}[](#l2.35)
if (Py_VerboseFlag)[](#l2.37)
PySys_WriteStderr("# zipimport: %s not cached, "[](#l2.38)
"reading TOC.\n", path);[](#l2.39)
files = read_directory(fp, buf);[](#l2.41)
fclose(fp);[](#l2.42)
if (files == NULL) {[](#l2.43)
Py_XDECREF(zip_stat);[](#l2.44)
return -1;[](#l2.45)
}[](#l2.46) if (PyDict_SetItemString(zip_directory_cache, path,[](#l2.47)
files) != 0)[](#l2.48)
files) != 0) {[](#l2.49)
Py_DECREF(files);[](#l2.50)
Py_XDECREF(zip_stat);[](#l2.51) return -1;[](#l2.52)
}[](#l2.53)
if (zip_stat && PyDict_SetItemString(zip_stat_cache, path,[](#l2.54)
zip_stat) != 0) {[](#l2.55)
Py_DECREF(files);[](#l2.56)
Py_DECREF(zip_stat);[](#l2.57)
return -1;[](#l2.58)
}[](#l2.59)
Py_XDECREF(zip_stat);[](#l2.60) }[](#l2.61) else[](#l2.62) Py_INCREF(files);[](#l2.63)
@@ -419,11 +451,12 @@ static PyObject * zipimporter_get_data(PyObject *obj, PyObject *args) { ZipImporter *self = (ZipImporter *)obj;
#ifdef ALTSEP char *p, buf[MAXPATHLEN + 1]; #endif
- PyObject *toc_entry, *data; Py_ssize_t len; if (!PyArg_ParseTuple(args, "s:zipimporter.get_data", &path)) @@ -448,12 +481,19 @@ zipimporter_get_data(PyObject *obj, PyOb path = path + len + 1; }
- fp = safely_reopen_archive(self, &archive);
- if (fp == NULL)
return NULL;[](#l2.85)
+ toc_entry = PyDict_GetItemString(self->files, path); if (toc_entry == NULL) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, path);
} static PyObject * @@ -473,7 +513,8 @@ zipimporter_get_source(PyObject *obj, Py { ZipImporter *self = (ZipImporter *)obj; PyObject *toc_entry;
- FILE *fp;
- char *fullname, *subname, path[MAXPATHLEN+1], *archive; int len; enum zi_module_info mi; @@ -501,13 +542,20 @@ zipimporter_get_source(PyObject *obj, Py else strcpy(path + len, ".py");
+ toc_entry = PyDict_GetItemString(self->files, path);
- if (toc_entry != NULL) {
PyObject *data = get_data(fp, archive, toc_entry);[](#l2.122)
fclose(fp);[](#l2.123)
return data;[](#l2.124)
- }
- fclose(fp);
/* we have the module, but no source */
} PyDoc_STRVAR(doc_find_module, @@ -662,7 +710,139 @@ get_long(unsigned char buf) { } /
- fopen_rb_and_stat(path, &py_stat) -> FILE * +
- Opens path in "rb" mode and populates the Python py_stat stat_result
- with information about the opened file. *py_stat may not be changed
- if there is no fstat_function or if fstat_function fails. +
- Returns NULL and does nothing to py_stat if the open failed. +/ +static FILE * +fopen_rb_and_stat(char *path, PyObject **py_stat_p) +{
- FILE *fp;
- assert(py_stat_p != NULL);
- assert(*py_stat_p == NULL);
- if (fstat_function) {
PyObject *stat_result = PyObject_CallFunction(fstat_function,[](#l2.161)
"i", fileno(fp));[](#l2.162)
if (stat_result == NULL) {[](#l2.163)
PyErr_Clear(); /* We can function without it. */[](#l2.164)
} else {[](#l2.165)
*py_stat_p = stat_result;[](#l2.166)
}[](#l2.167)
- }
+} + +/* Return 1 if objects a and b fail a Py_EQ test for an attr. */ +static int +compare_obj_attr_strings(PyObject *obj_a, PyObject *obj_b, char *attr_name) +{
- int problem = 0;
- PyObject *attr_a = PyObject_GetAttrString(obj_a, attr_name);
- PyObject *attr_b = PyObject_GetAttrString(obj_b, attr_name);
- if (attr_a == NULL || attr_b == NULL)
problem = 1;[](#l2.181)
- else
problem = (PyObject_RichCompareBool(attr_a, attr_b, Py_EQ) != 1);[](#l2.183)
- Py_XDECREF(attr_a);
- Py_XDECREF(attr_b);
- return problem;
- *
- */ +static FILE * +safely_reopen_archive(ZipImporter *self, char **archive_p) +{
- FILE *fp;
- PyObject *stat_now = NULL;
- char *archive;
- assert(archive_p != NULL);
- *archive_p = PyString_AsString(self->archive);
- if (*archive_p == NULL)
return NULL;[](#l2.206)
- archive = *archive_p;
- fp = fopen_rb_and_stat(archive, &stat_now);
- if (!fp) {
PyErr_Format(PyExc_IOError,[](#l2.211)
"zipimport: can not open file %s", archive);[](#l2.212)
Py_XDECREF(stat_now);[](#l2.213)
return NULL;[](#l2.214)
- }
- if (stat_now != NULL) {
int problem = 0;[](#l2.218)
PyObject *files;[](#l2.219)
PyObject *prev_stat = PyDict_GetItemString(zip_stat_cache, archive);[](#l2.220)
/* Test stat_now vs the old cached stat on some key attributes. */[](#l2.221)
if (prev_stat != NULL) {[](#l2.222)
problem = compare_obj_attr_strings(prev_stat, stat_now,[](#l2.223)
"st_ino");[](#l2.224)
problem |= compare_obj_attr_strings(prev_stat, stat_now,[](#l2.225)
"st_size");[](#l2.226)
problem |= compare_obj_attr_strings(prev_stat, stat_now,[](#l2.227)
"st_mtime");[](#l2.228)
} else {[](#l2.229)
if (Py_VerboseFlag)[](#l2.230)
PySys_WriteStderr("# zipimport: no stat data for %s!\n",[](#l2.231)
archive);[](#l2.232)
problem = 1;[](#l2.233)
}[](#l2.234)
if (problem) {[](#l2.236)
if (Py_VerboseFlag)[](#l2.237)
PySys_WriteStderr("# zipimport: %s modified since last"[](#l2.238)
" import, rereading TOC.\n", archive);[](#l2.239)
files = read_directory(fp, archive);[](#l2.240)
if (files == NULL) {[](#l2.241)
Py_DECREF(stat_now);[](#l2.242)
fclose(fp);[](#l2.243)
return NULL;[](#l2.244)
}[](#l2.245)
if (PyDict_SetItem(zip_directory_cache, self->archive,[](#l2.246)
files) != 0) {[](#l2.247)
Py_DECREF(files);[](#l2.248)
Py_DECREF(stat_now);[](#l2.249)
fclose(fp);[](#l2.250)
return NULL;[](#l2.251)
}[](#l2.252)
if (stat_now && PyDict_SetItem(zip_stat_cache, self->archive,[](#l2.253)
stat_now) != 0) {[](#l2.254)
Py_DECREF(files);[](#l2.255)
Py_DECREF(stat_now);[](#l2.256)
fclose(fp);[](#l2.257)
return NULL;[](#l2.258)
}[](#l2.259)
Py_XDECREF(self->files); /* free the old value. */[](#l2.260)
self->files = files;[](#l2.261)
} else {[](#l2.262)
/* No problem, discard the new stat data. */[](#l2.263)
Py_DECREF(stat_now);[](#l2.264)
}[](#l2.265)
- } /* stat succeeded */
- read_directory(fp, archive) -> files dict (new reference) Given a path to a Zip archive, build a dict, mapping file names (local to the archive, using SEP as a separator) to toc entries. @@ -683,10 +863,9 @@ get_long(unsigned char *buf) { data_size and file_offset are 0. */ static PyObject * -read_directory(char *archive) +read_directory(FILE *fp, char *archive) { PyObject *files = NULL;
- FILE *fp; long compress, crc, data_size, file_size, file_offset, date, time; long header_offset, name_size, header_size, header_position; long i, l, count; @@ -696,6 +875,7 @@ read_directory(char *archive) char p, endof_central_dir[22]; long arc_offset; / offset from beginning of file to start of zip-archive */
- assert(fp != NULL); if (strlen(archive) > MAXPATHLEN) { PyErr_SetString(PyExc_OverflowError, "Zip path name is too long");
@@ -703,28 +883,18 @@ read_directory(char *archive) } strcpy(path, archive);
- fp = fopen(archive, "rb");
- if (fp == NULL) {
PyErr_Format(ZipImportError, "can't open Zip file: "[](#l2.302)
"'%.200s'", archive);[](#l2.303)
return NULL;[](#l2.304)
- }
- if (fseek(fp, -22, SEEK_END) == -1) {
} header_position = ftell(fp); if (fread(endof_central_dir, 1, 22, fp) != 22) {fclose(fp);[](#l2.308) PyErr_Format(ZipImportError, "can't read Zip file: %s", archive);[](#l2.309) return NULL;[](#l2.310)
} if (get_long((unsigned char )endof_central_dir) != 0x06054B50) { / Bad: End of Central Dir signature */fclose(fp);[](#l2.314) PyErr_Format(ZipImportError, "can't read Zip file: "[](#l2.315) "'%.200s'", archive);[](#l2.316) return NULL;[](#l2.317)
fclose(fp);[](#l2.321) PyErr_Format(ZipImportError, "not a Zip file: "[](#l2.322) "'%.200s'", archive);[](#l2.323) return NULL;[](#l2.324)
@@ -793,18 +963,15 @@ read_directory(char *archive) goto error; count++; }
- fclose(fp); if (Py_VerboseFlag) PySys_WriteStderr("# zipimport: found %ld names in %s\n", count, archive); return files; fseek_error:
- fclose(fp); Py_XDECREF(files); PyErr_Format(ZipImportError, "can't read Zip file: %s", archive); return NULL; error:
- fclose(fp); Py_XDECREF(files); return NULL; }
@@ -841,14 +1008,13 @@ get_decompress_func(void) return decompress; } -/* Given a path to a Zip file and a toc_entry, return the (uncompressed) +/* Given a FILE* to a Zip file and a toc_entry, return the (uncompressed) data as a new reference. */ static PyObject * -get_data(char *archive, PyObject *toc_entry) +get_data(FILE *fp, char *archive, PyObject *toc_entry) { PyObject *raw_data, *data = NULL, *decompress; char *buf;
- FILE *fp; int err; Py_ssize_t bytes_read = 0; long l; @@ -862,16 +1028,8 @@ get_data(char *archive, PyObject *toc_en return NULL; }
- fp = fopen(archive, "rb");
- if (!fp) {
PyErr_Format(PyExc_IOError,[](#l2.367)
"zipimport: can not open file %s", archive);[](#l2.368)
return NULL;[](#l2.369)
- }
- /* Check to make sure the local file header is correct */ if (fseek(fp, file_offset, 0) == -1) {
} @@ -882,11 +1040,9 @@ get_data(char *archive, PyObject *toc_en PyErr_Format(ZipImportError, "bad local file header in %s", archive);fclose(fp);[](#l2.374) PyErr_Format(ZipImportError, "can't read Zip file: %s", archive);[](#l2.375) return NULL;[](#l2.376)
} if (fseek(fp, file_offset + 26, 0) == -1) {fclose(fp);[](#l2.382) return NULL;[](#l2.383)
} @@ -898,7 +1054,6 @@ get_data(char *archive, PyObject *toc_en raw_data = PyString_FromStringAndSize((char *)NULL, compress == 0 ? data_size : data_size + 1); if (raw_data == NULL) {fclose(fp);[](#l2.386) PyErr_Format(ZipImportError, "can't read Zip file: %s", archive);[](#l2.387) return NULL;[](#l2.388)
} buf = PyString_AsString(raw_data); @@ -907,11 +1062,9 @@ get_data(char *archive, PyObject *toc_en if (err == 0) { bytes_read = fread(buf, 1, data_size, fp); } else {fclose(fp);[](#l2.394) return NULL;[](#l2.395)
}fclose(fp);[](#l2.402) PyErr_Format(ZipImportError, "can't read Zip file: %s", archive);[](#l2.403) return NULL;[](#l2.404)
- fclose(fp); if (err || bytes_read != data_size) { PyErr_SetString(PyExc_IOError, "zipimport: can't read data");
@@ -1107,17 +1260,13 @@ get_mtime_of_source(ZipImporter self, c / Return the code object for the module named by 'fullname' from the Zip archive as a new reference. */ static PyObject * -get_code_from_data(ZipImporter *self, int ispackage, int isbytecode,
time_t mtime, PyObject *toc_entry)[](#l2.415)
+get_code_from_data(char *archive, FILE *fp, int ispackage,
int isbytecode, time_t mtime, PyObject *toc_entry)[](#l2.417)
{ PyObject *data, *code; char *modpath;
@@ -1152,12 +1301,19 @@ get_module_code(ZipImporter *self, char for (zso = zip_searchorder; *zso->suffix; zso++) { PyObject *code = NULL;
FILE *fp;[](#l2.435)
char *archive;[](#l2.436)
strcpy(path + len, zso->suffix); if (Py_VerboseFlag > 1) PySys_WriteStderr("# trying %s%c%s\n", PyString_AsString(self->archive), SEP, path); +
fp = safely_reopen_archive(self, &archive);[](#l2.444)
if (fp == NULL)[](#l2.445)
return NULL;[](#l2.446)
+ toc_entry = PyDict_GetItemString(self->files, path); if (toc_entry != NULL) { time_t mtime = 0; @@ -1168,9 +1324,10 @@ get_module_code(ZipImporter *self, char mtime = get_mtime_of_source(self, path); if (p_ispackage != NULL) *p_ispackage = ispackage;
code = get_code_from_data(self, ispackage,[](#l2.455)
code = get_code_from_data(archive, fp, ispackage,[](#l2.456) isbytecode, mtime,[](#l2.457) toc_entry);[](#l2.458)
fclose(fp);[](#l2.459) if (code == Py_None) {[](#l2.460) /* bad magic number or non-matching mtime[](#l2.461) in byte code, try next */[](#l2.462)
@@ -1182,6 +1339,7 @@ get_module_code(ZipImporter *self, char PyTuple_GetItem(toc_entry, 0)); return code; }
} PyErr_Format(ZipImportError, "can't find module '%.200s'", fullname); return NULL; @@ -1199,6 +1357,8 @@ This module exports three objects:\n[](#l2.471) subclass of ImportError, so it can be caught as ImportError, too.\n[](#l2.472)fclose(fp);[](#l2.467)
- _zip_directory_cache: a dict, mapping archive paths to zip directory\n[](#l2.473) info dicts, as used in zipimporter._files.\n[](#l2.474) +- _zip_stat_cache: a dict, mapping archive paths to stat_result\n[](#l2.475)
- info for the .zip the last time anything was imported from it.\n[](#l2.476) \n[](#l2.477) It is usually not needed to use the zipimport module explicitly; it is\n[](#l2.478) used by the builtin import mechanism for sys.path items that are paths\n[](#l2.479) @@ -1247,6 +1407,7 @@ initzipimport(void) (PyObject *)&ZipImporter_Type) < 0) return;
- Py_XDECREF(zip_directory_cache); /* Avoid embedded interpreter leaks. */ zip_directory_cache = PyDict_New(); if (zip_directory_cache == NULL) return;
@@ -1254,4 +1415,31 @@ initzipimport(void) if (PyModule_AddObject(mod, "_zip_directory_cache", zip_directory_cache) < 0) return; +
- Py_XDECREF(zip_stat_cache); /* Avoid embedded interpreter leaks. */
- zip_stat_cache = PyDict_New();
- if (zip_stat_cache == NULL)
return;[](#l2.496)
- Py_INCREF(zip_stat_cache);
- if (PyModule_AddObject(mod, "_zip_stat_cache", zip_stat_cache) < 0)
return;[](#l2.499)
- {
/* We cannot import "os" here as that is a .py/.pyc file that could[](#l2.502)
* live within a zipped up standard library. Import the posix or nt[](#l2.503)
* builtin that provides the fstat() function we want instead. */[](#l2.504)
PyObject *os_like_module;[](#l2.505)
Py_XDECREF(fstat_function); /* Avoid embedded interpreter leaks. */[](#l2.506)
os_like_module = PyImport_ImportModule("posix");[](#l2.507)
if (os_like_module == NULL) {[](#l2.508)
os_like_module = PyImport_ImportModule("nt");[](#l2.509)
}[](#l2.510)
if (os_like_module != NULL) {[](#l2.511)
fstat_function = PyObject_GetAttrString(os_like_module, "fstat");[](#l2.512)
Py_DECREF(os_like_module);[](#l2.513)
}[](#l2.514)
if (fstat_function == NULL) {[](#l2.515)
PyErr_Clear(); /* non-fatal, we'll go on without it. */[](#l2.516)
}[](#l2.517)
- }