~ubuntu-branches/debian/sid/meliae/sid

« back to all changes in this revision

Viewing changes to meliae/files.py

  • Committer: Bazaar Package Importer
  • Author(s): Jelmer Vernooij
  • Date: 2009-12-19 18:23:37 UTC
  • Revision ID: james.westby@ubuntu.com-20091219182337-t09txw6ca1yfysn9
Tags: upstream-0.2.0
ImportĀ upstreamĀ versionĀ 0.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2009 Canonical Ltd
 
2
 
3
# This program is free software: you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License version 3 as
 
5
# published by the Free Software Foundation.
 
6
 
7
# This program is distributed in the hope that it will be useful, but
 
8
# WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
10
# General Public License for more details.
 
11
 
12
# You should have received a copy of the GNU General Public License
 
13
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
14
 
 
15
"""Work with files on disk."""
 
16
 
 
17
import errno
 
18
import gzip
 
19
try:
 
20
    import multiprocessing
 
21
except ImportError:
 
22
    multiprocessing = None
 
23
import subprocess
 
24
import sys
 
25
 
 
26
 
 
27
def open_file(filename):
 
28
    """Open a file which might be a regular file or a gzip.
 
29
    
 
30
    :return: An iterator of lines, and a cleanup function.
 
31
    """
 
32
    source = open(filename, 'rb')
 
33
    gzip_source = gzip.GzipFile(mode='rb', fileobj=source)
 
34
    try:
 
35
        line = gzip_source.readline()
 
36
    except KeyboardInterrupt:
 
37
        raise
 
38
    except:
 
39
        # probably not a gzip file
 
40
        source.seek(0)
 
41
        return source, None
 
42
    else:
 
43
        # We don't need these anymore, so close them out in case the rest of
 
44
        # the code raises an exception.
 
45
        gzip_source.close()
 
46
        source.close()
 
47
        # a gzip file
 
48
        # preference - a gzip subprocess
 
49
        if sys.platform == 'win32':
 
50
            close_fds = False # not supported
 
51
        else:
 
52
            close_fds = True
 
53
        try:
 
54
            process = subprocess.Popen(['gzip', '-d', '-c', filename],
 
55
                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
 
56
                stderr=subprocess.PIPE, close_fds=close_fds)
 
57
        except OSError, e:
 
58
            if e.errno == errno.ENOENT:
 
59
                # failing that, use another python process
 
60
                return _open_mprocess(filename)
 
61
        # make reading from stdin, or writting errors cause immediate aborts
 
62
        process.stdin.close()
 
63
        process.stderr.close()
 
64
        terminate = getattr(process, 'terminate', None)
 
65
        # terminate is a py2.6 thing
 
66
        if terminate is not None:
 
67
            return process.stdout, terminate
 
68
        else:
 
69
            # We would like to use process.wait() but that can cause a deadlock
 
70
            # if the child is still writing.
 
71
            # The other alternative is process.communicate, but we closed
 
72
            # stderr, and communicate wants to read from it. (We get:
 
73
            #  ValueError: I/O operation on closed file
 
74
            # if we try it here. Also, for large files, this may be many GB
 
75
            # worth of data.
 
76
            # So for now, live with the deadlock...
 
77
            return process.stdout, process.wait
 
78
 
 
79
 
 
80
def _stream_file(filename, child):
 
81
    gzip_source = gzip.GzipFile(filename, 'rb')
 
82
    for line in gzip_source:
 
83
        child.send(line)
 
84
    child.send(None)
 
85
 
 
86
 
 
87
def _open_mprocess(filename):
 
88
    if multiprocessing is None:
 
89
        # can't multiprocess, use inprocess gzip.
 
90
        return gzip.GzipFile(filename, mode='rb'), None
 
91
    parent, child = multiprocessing.Pipe(False)
 
92
    process = multiprocessing.Process(target=_stream_file, args=(filename, child))
 
93
    process.start()
 
94
    def iter_pipe():
 
95
        while True:
 
96
            line = parent.recv()
 
97
            if line is None:
 
98
                break
 
99
            yield line
 
100
    return iter_pipe(), process.join