summaryrefslogtreecommitdiffstats
path: root/platform_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'platform_utils.py')
-rw-r--r--platform_utils.py183
1 files changed, 16 insertions, 167 deletions
diff --git a/platform_utils.py b/platform_utils.py
index 06ef9b18..0203249a 100644
--- a/platform_utils.py
+++ b/platform_utils.py
@@ -1,5 +1,3 @@
1# -*- coding:utf-8 -*-
2#
3# Copyright (C) 2016 The Android Open Source Project 1# Copyright (C) 2016 The Android Open Source Project
4# 2#
5# Licensed under the Apache License, Version 2.0 (the "License"); 3# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,18 +15,9 @@
17import errno 15import errno
18import os 16import os
19import platform 17import platform
20import select
21import shutil 18import shutil
22import stat 19import stat
23 20
24from pyversion import is_python3
25if is_python3():
26 from queue import Queue
27else:
28 from Queue import Queue
29
30from threading import Thread
31
32 21
33def isWindows(): 22def isWindows():
34 """ Returns True when running with the native port of Python for Windows, 23 """ Returns True when running with the native port of Python for Windows,
@@ -39,145 +28,6 @@ def isWindows():
39 return platform.system() == "Windows" 28 return platform.system() == "Windows"
40 29
41 30
42class FileDescriptorStreams(object):
43 """ Platform agnostic abstraction enabling non-blocking I/O over a
44 collection of file descriptors. This abstraction is required because
45 fctnl(os.O_NONBLOCK) is not supported on Windows.
46 """
47 @classmethod
48 def create(cls):
49 """ Factory method: instantiates the concrete class according to the
50 current platform.
51 """
52 if isWindows():
53 return _FileDescriptorStreamsThreads()
54 else:
55 return _FileDescriptorStreamsNonBlocking()
56
57 def __init__(self):
58 self.streams = []
59
60 def add(self, fd, dest, std_name):
61 """ Wraps an existing file descriptor as a stream.
62 """
63 self.streams.append(self._create_stream(fd, dest, std_name))
64
65 def remove(self, stream):
66 """ Removes a stream, when done with it.
67 """
68 self.streams.remove(stream)
69
70 @property
71 def is_done(self):
72 """ Returns True when all streams have been processed.
73 """
74 return len(self.streams) == 0
75
76 def select(self):
77 """ Returns the set of streams that have data available to read.
78 The returned streams each expose a read() and a close() method.
79 When done with a stream, call the remove(stream) method.
80 """
81 raise NotImplementedError
82
83 def _create_stream(self, fd, dest, std_name):
84 """ Creates a new stream wrapping an existing file descriptor.
85 """
86 raise NotImplementedError
87
88
89class _FileDescriptorStreamsNonBlocking(FileDescriptorStreams):
90 """ Implementation of FileDescriptorStreams for platforms that support
91 non blocking I/O.
92 """
93 class Stream(object):
94 """ Encapsulates a file descriptor """
95 def __init__(self, fd, dest, std_name):
96 self.fd = fd
97 self.dest = dest
98 self.std_name = std_name
99 self.set_non_blocking()
100
101 def set_non_blocking(self):
102 import fcntl
103 flags = fcntl.fcntl(self.fd, fcntl.F_GETFL)
104 fcntl.fcntl(self.fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
105
106 def fileno(self):
107 return self.fd.fileno()
108
109 def read(self):
110 return self.fd.read(4096)
111
112 def close(self):
113 self.fd.close()
114
115 def _create_stream(self, fd, dest, std_name):
116 return self.Stream(fd, dest, std_name)
117
118 def select(self):
119 ready_streams, _, _ = select.select(self.streams, [], [])
120 return ready_streams
121
122
123class _FileDescriptorStreamsThreads(FileDescriptorStreams):
124 """ Implementation of FileDescriptorStreams for platforms that don't support
125 non blocking I/O. This implementation requires creating threads issuing
126 blocking read operations on file descriptors.
127 """
128 def __init__(self):
129 super(_FileDescriptorStreamsThreads, self).__init__()
130 # The queue is shared accross all threads so we can simulate the
131 # behavior of the select() function
132 self.queue = Queue(10) # Limit incoming data from streams
133
134 def _create_stream(self, fd, dest, std_name):
135 return self.Stream(fd, dest, std_name, self.queue)
136
137 def select(self):
138 # Return only one stream at a time, as it is the most straighforward
139 # thing to do and it is compatible with the select() function.
140 item = self.queue.get()
141 stream = item.stream
142 stream.data = item.data
143 return [stream]
144
145 class QueueItem(object):
146 """ Item put in the shared queue """
147 def __init__(self, stream, data):
148 self.stream = stream
149 self.data = data
150
151 class Stream(object):
152 """ Encapsulates a file descriptor """
153 def __init__(self, fd, dest, std_name, queue):
154 self.fd = fd
155 self.dest = dest
156 self.std_name = std_name
157 self.queue = queue
158 self.data = None
159 self.thread = Thread(target=self.read_to_queue)
160 self.thread.daemon = True
161 self.thread.start()
162
163 def close(self):
164 self.fd.close()
165
166 def read(self):
167 data = self.data
168 self.data = None
169 return data
170
171 def read_to_queue(self):
172 """ The thread function: reads everything from the file descriptor into
173 the shared queue and terminates when reaching EOF.
174 """
175 for line in iter(self.fd.readline, b''):
176 self.queue.put(_FileDescriptorStreamsThreads.QueueItem(self, line))
177 self.fd.close()
178 self.queue.put(_FileDescriptorStreamsThreads.QueueItem(self, None))
179
180
181def symlink(source, link_name): 31def symlink(source, link_name):
182 """Creates a symbolic link pointing to source named link_name. 32 """Creates a symbolic link pointing to source named link_name.
183 Note: On Windows, source must exist on disk, as the implementation needs 33 Note: On Windows, source must exist on disk, as the implementation needs
@@ -274,31 +124,30 @@ def rename(src, dst):
274 else: 124 else:
275 raise 125 raise
276 else: 126 else:
277 os.rename(src, dst) 127 shutil.move(src, dst)
278 128
279 129
280def remove(path): 130def remove(path, missing_ok=False):
281 """Remove (delete) the file path. This is a replacement for os.remove that 131 """Remove (delete) the file path. This is a replacement for os.remove that
282 allows deleting read-only files on Windows, with support for long paths and 132 allows deleting read-only files on Windows, with support for long paths and
283 for deleting directory symbolic links. 133 for deleting directory symbolic links.
284 134
285 Availability: Unix, Windows.""" 135 Availability: Unix, Windows."""
286 if isWindows(): 136 longpath = _makelongpath(path) if isWindows() else path
287 longpath = _makelongpath(path) 137 try:
288 try: 138 os.remove(longpath)
289 os.remove(longpath) 139 except OSError as e:
290 except OSError as e: 140 if e.errno == errno.EACCES:
291 if e.errno == errno.EACCES: 141 os.chmod(longpath, stat.S_IWRITE)
292 os.chmod(longpath, stat.S_IWRITE) 142 # Directory symbolic links must be deleted with 'rmdir'.
293 # Directory symbolic links must be deleted with 'rmdir'. 143 if islink(longpath) and isdir(longpath):
294 if islink(longpath) and isdir(longpath): 144 os.rmdir(longpath)
295 os.rmdir(longpath)
296 else:
297 os.remove(longpath)
298 else: 145 else:
299 raise 146 os.remove(longpath)
300 else: 147 elif missing_ok and e.errno == errno.ENOENT:
301 os.remove(path) 148 pass
149 else:
150 raise
302 151
303 152
304def walk(top, topdown=True, onerror=None, followlinks=False): 153def walk(top, topdown=True, onerror=None, followlinks=False):