| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | """distutils.file_util
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:03:34 +00:00
										 |  |  | Utility functions for operating on single files. | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | from distutils.errors import DistutilsFileError | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  | from distutils import log | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # for generating verbose output in 'copy_file()' | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +00:00
										 |  |  | _copy_action = { None:   'copying', | 
					
						
							|  |  |  |                  'hard': 'hard linking', | 
					
						
							|  |  |  |                  'sym':  'symbolically linking' } | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  | def _copy_file_contents(src, dst, buffer_size=16*1024): | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +00:00
										 |  |  |     """Copy the file 'src' to 'dst'; both must be filenames.  Any error
 | 
					
						
							|  |  |  |     opening either file, reading from 'src', or writing to 'dst', raises | 
					
						
							|  |  |  |     DistutilsFileError.  Data is read/written in chunks of 'buffer_size' | 
					
						
							|  |  |  |     bytes (default 16k).  No attempt is made to handle anything apart from | 
					
						
							|  |  |  |     regular files. | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     # Stolen from shutil module in the standard library, but with | 
					
						
							|  |  |  |     # custom error-handling added. | 
					
						
							|  |  |  |     fsrc = None | 
					
						
							|  |  |  |     fdst = None | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             fsrc = open(src, 'rb') | 
					
						
							| 
									
										
										
										
											2012-12-18 22:02:39 +02:00
										 |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2008-10-05 09:00:28 +00:00
										 |  |  |             raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror)) | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-02-01 18:29:34 +00:00
										 |  |  |         if os.path.exists(dst): | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 os.unlink(dst) | 
					
						
							| 
									
										
										
										
											2012-12-18 22:02:39 +02:00
										 |  |  |             except OSError as e: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise DistutilsFileError( | 
					
						
							| 
									
										
										
										
											2008-10-05 09:00:28 +00:00
										 |  |  |                       "could not delete '%s': %s" % (dst, e.strerror)) | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             fdst = open(dst, 'wb') | 
					
						
							| 
									
										
										
										
											2012-12-18 22:02:39 +02:00
										 |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             raise DistutilsFileError( | 
					
						
							| 
									
										
										
										
											2008-10-05 09:00:28 +00:00
										 |  |  |                   "could not create '%s': %s" % (dst, e.strerror)) | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |         while True: | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |                 buf = fsrc.read(buffer_size) | 
					
						
							| 
									
										
										
										
											2012-12-18 22:02:39 +02:00
										 |  |  |             except OSError as e: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise DistutilsFileError( | 
					
						
							| 
									
										
										
										
											2008-10-05 09:00:28 +00:00
										 |  |  |                       "could not read from '%s': %s" % (src, e.strerror)) | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |             if not buf: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 fdst.write(buf) | 
					
						
							| 
									
										
										
										
											2012-12-18 22:02:39 +02:00
										 |  |  |             except OSError as e: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise DistutilsFileError( | 
					
						
							| 
									
										
										
										
											2008-10-05 09:00:28 +00:00
										 |  |  |                       "could not write to '%s': %s" % (dst, e.strerror)) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         if fdst: | 
					
						
							|  |  |  |             fdst.close() | 
					
						
							|  |  |  |         if fsrc: | 
					
						
							|  |  |  |             fsrc.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  | def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, | 
					
						
							| 
									
										
										
										
											2009-02-06 00:38:35 +00:00
										 |  |  |               link=None, verbose=1, dry_run=0): | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +00:00
										 |  |  |     """Copy a file 'src' to 'dst'.  If 'dst' is a directory, then 'src' is
 | 
					
						
							|  |  |  |     copied there with the same name; otherwise, it must be a filename.  (If | 
					
						
							|  |  |  |     the file exists, it will be ruthlessly clobbered.)  If 'preserve_mode' | 
					
						
							|  |  |  |     is true (the default), the file's mode (type and permission bits, or | 
					
						
							|  |  |  |     whatever is analogous on the current platform) is copied.  If | 
					
						
							|  |  |  |     'preserve_times' is true (the default), the last-modified and | 
					
						
							|  |  |  |     last-access times are copied as well.  If 'update' is true, 'src' will | 
					
						
							|  |  |  |     only be copied if 'dst' does not exist, or if 'dst' does exist but is | 
					
						
							|  |  |  |     older than 'src'. | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     'link' allows you to make hard links (os.link) or symbolic links | 
					
						
							|  |  |  |     (os.symlink) instead of copying: set it to "hard" or "sym"; if it is | 
					
						
							|  |  |  |     None (the default), files are copied.  Don't set 'link' on systems that | 
					
						
							|  |  |  |     don't support it: 'copy_file()' doesn't check if hard or symbolic | 
					
						
							| 
									
										
										
										
											2014-10-30 19:37:07 +01:00
										 |  |  |     linking is available. If hardlink fails, falls back to | 
					
						
							|  |  |  |     _copy_file_contents(). | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Under Mac OS, uses the native file copy function in macostools; on | 
					
						
							|  |  |  |     other systems, uses '_copy_file_contents()' to copy file contents. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-30 17:29:35 +00:00
										 |  |  |     Return a tuple (dest_name, copied): 'dest_name' is the actual name of | 
					
						
							|  |  |  |     the output file, and 'copied' is true if the file was copied (or would | 
					
						
							|  |  |  |     have been copied, if 'dry_run' true). | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     # XXX if the destination file already exists, we clobber it if | 
					
						
							|  |  |  |     # copying, but blow up if linking.  Hmmm.  And I don't know what | 
					
						
							|  |  |  |     # macostools.copyfile() does.  Should definitely be consistent, and | 
					
						
							|  |  |  |     # should probably blow up if destination exists and we would be | 
					
						
							|  |  |  |     # changing it (ie. it's not already a hard/soft link to src OR | 
					
						
							|  |  |  |     # (not update) and (src newer than dst). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     from distutils.dep_util import newer | 
					
						
							| 
									
										
										
										
											2001-07-25 19:48:03 +00:00
										 |  |  |     from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     if not os.path.isfile(src): | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |         raise DistutilsFileError( | 
					
						
							|  |  |  |               "can't copy '%s': doesn't exist or not a regular file" % src) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     if os.path.isdir(dst): | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |         dir = dst | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |         dst = os.path.join(dst, os.path.basename(src)) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |         dir = os.path.dirname(dst) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     if update and not newer(src, dst): | 
					
						
							| 
									
										
										
										
											2009-02-06 00:53:43 +00:00
										 |  |  |         if verbose >= 1: | 
					
						
							| 
									
										
										
										
											2009-02-06 00:38:35 +00:00
										 |  |  |             log.debug("not copying %s (output up-to-date)", src) | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |         return (dst, 0) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         action = _copy_action[link] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |         raise ValueError("invalid value '%s' for 'link' argument" % link) | 
					
						
							| 
									
										
										
										
											2009-02-06 00:38:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-06 00:53:43 +00:00
										 |  |  |     if verbose >= 1: | 
					
						
							| 
									
										
										
										
											2009-02-06 00:38:35 +00:00
										 |  |  |         if os.path.basename(dst) == os.path.basename(src): | 
					
						
							|  |  |  |             log.info("%s %s -> %s", action, src, dir) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             log.info("%s %s -> %s", action, src, dst) | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     if dry_run: | 
					
						
							| 
									
										
										
										
											2000-09-30 17:29:35 +00:00
										 |  |  |         return (dst, 1) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # If linking (hard or symbolic), use the appropriate system call | 
					
						
							|  |  |  |     # (Unix only, of course, but that's the caller's responsibility) | 
					
						
							|  |  |  |     elif link == 'hard': | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |         if not (os.path.exists(dst) and os.path.samefile(src, dst)): | 
					
						
							| 
									
										
										
										
											2014-10-30 19:37:07 +01:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 os.link(src, dst) | 
					
						
							|  |  |  |                 return (dst, 1) | 
					
						
							|  |  |  |             except OSError: | 
					
						
							|  |  |  |                 # If hard linking fails, fall back on copying file | 
					
						
							|  |  |  |                 # (some special filesystems don't support hard linking | 
					
						
							|  |  |  |                 #  even under Unix, see issue #8876). | 
					
						
							|  |  |  |                 pass | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     elif link == 'sym': | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |         if not (os.path.exists(dst) and os.path.samefile(src, dst)): | 
					
						
							|  |  |  |             os.symlink(src, dst) | 
					
						
							| 
									
										
										
										
											2014-10-30 19:37:07 +01:00
										 |  |  |             return (dst, 1) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Otherwise (non-Mac, not linking), copy the file contents and | 
					
						
							|  |  |  |     # (optionally) copy the times and mode. | 
					
						
							| 
									
										
										
										
											2014-10-30 19:37:07 +01:00
										 |  |  |     _copy_file_contents(src, dst) | 
					
						
							|  |  |  |     if preserve_mode or preserve_times: | 
					
						
							|  |  |  |         st = os.stat(src) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # According to David Ascher <da@ski.org>, utime() should be done | 
					
						
							|  |  |  |         # before chmod() (at least under NT). | 
					
						
							|  |  |  |         if preserve_times: | 
					
						
							|  |  |  |             os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) | 
					
						
							|  |  |  |         if preserve_mode: | 
					
						
							|  |  |  |             os.chmod(dst, S_IMODE(st[ST_MODE])) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-30 17:29:35 +00:00
										 |  |  |     return (dst, 1) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # XXX I suspect this is Unix-specific -- need porting help! | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +00:00
										 |  |  | def move_file (src, dst, | 
					
						
							|  |  |  |                verbose=1, | 
					
						
							|  |  |  |                dry_run=0): | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +00:00
										 |  |  |     """Move a file 'src' to 'dst'.  If 'dst' is a directory, the file will
 | 
					
						
							|  |  |  |     be moved into it with the same name; otherwise, 'src' is just renamed | 
					
						
							|  |  |  |     to 'dst'.  Return the new full name of the file. | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     Handles cross-device moves on Unix using 'copy_file()'.  What about | 
					
						
							|  |  |  |     other systems??? | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     from os.path import exists, isfile, isdir, basename, dirname | 
					
						
							| 
									
										
										
										
											2001-08-09 20:59:53 +00:00
										 |  |  |     import errno | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-06 00:53:43 +00:00
										 |  |  |     if verbose >= 1: | 
					
						
							| 
									
										
										
										
											2009-02-06 00:38:35 +00:00
										 |  |  |         log.info("moving %s -> %s", src, dst) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if dry_run: | 
					
						
							|  |  |  |         return dst | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     if not isfile(src): | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |         raise DistutilsFileError("can't move '%s': not a regular file" % src) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     if isdir(dst): | 
					
						
							|  |  |  |         dst = os.path.join(dst, basename(src)) | 
					
						
							|  |  |  |     elif exists(dst): | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |         raise DistutilsFileError( | 
					
						
							|  |  |  |               "can't move '%s': destination '%s' already exists" % | 
					
						
							|  |  |  |               (src, dst)) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     if not isdir(dirname(dst)): | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |         raise DistutilsFileError( | 
					
						
							|  |  |  |               "can't move '%s': destination '%s' not a valid path" % | 
					
						
							|  |  |  |               (src, dst)) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     copy_it = False | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |         os.rename(src, dst) | 
					
						
							| 
									
										
										
										
											2012-12-18 22:02:39 +02:00
										 |  |  |     except OSError as e: | 
					
						
							| 
									
										
										
										
											2014-08-29 07:07:35 +03:00
										 |  |  |         (num, msg) = e.args | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |         if num == errno.EXDEV: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             copy_it = True | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             raise DistutilsFileError( | 
					
						
							|  |  |  |                   "couldn't move '%s' to '%s': %s" % (src, dst, msg)) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if copy_it: | 
					
						
							| 
									
										
										
										
											2009-02-06 00:38:35 +00:00
										 |  |  |         copy_file(src, dst, verbose=verbose) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |             os.unlink(src) | 
					
						
							| 
									
										
										
										
											2012-12-18 22:02:39 +02:00
										 |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2014-08-29 07:07:35 +03:00
										 |  |  |             (num, msg) = e.args | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |                 os.unlink(dst) | 
					
						
							| 
									
										
										
										
											2012-12-18 22:02:39 +02:00
										 |  |  |             except OSError: | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |                 pass | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             raise DistutilsFileError( | 
					
						
							|  |  |  |                   "couldn't move '%s' to '%s' by copy/delete: " | 
					
						
							|  |  |  |                   "delete '%s' failed: %s" | 
					
						
							|  |  |  |                   % (src, dst, src, msg)) | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     return dst | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +00:00
										 |  |  | def write_file (filename, contents): | 
					
						
							| 
									
										
										
										
											2000-04-04 02:05:59 +00:00
										 |  |  |     """Create a file with the specified name and write 'contents' (a
 | 
					
						
							| 
									
										
										
										
											2000-09-23 00:59:34 +00:00
										 |  |  |     sequence of strings without line terminators) to it. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     f = open(filename, "w") | 
					
						
							| 
									
										
										
										
											2010-11-05 23:51:56 +00:00
										 |  |  |     try: | 
					
						
							|  |  |  |         for line in contents: | 
					
						
							|  |  |  |             f.write(line + "\n") | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         f.close() |