| Conditions | 4 |
| Total Lines | 66 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | #!/usr/bin/env python |
||
| 96 | @contextmanager |
||
| 97 | def tee_output_fd(): |
||
| 98 | """Duplicate stdout and stderr to a file on the file descriptor level.""" |
||
| 99 | with NamedTemporaryFile() as target: |
||
| 100 | original_stdout_fd = 1 |
||
| 101 | original_stderr_fd = 2 |
||
| 102 | |||
| 103 | # Save a copy of the original stdout and stderr file descriptors |
||
| 104 | saved_stdout_fd = os.dup(original_stdout_fd) |
||
| 105 | saved_stderr_fd = os.dup(original_stderr_fd) |
||
| 106 | |||
| 107 | target_fd = target.fileno() |
||
| 108 | |||
| 109 | final_output = [] |
||
| 110 | |||
| 111 | try: |
||
| 112 | tee_stdout = subprocess.Popen( |
||
| 113 | ['tee', '-a', '/dev/stderr'], |
||
| 114 | stdin=subprocess.PIPE, stderr=target_fd, stdout=1) |
||
| 115 | tee_stderr = subprocess.Popen( |
||
| 116 | ['tee', '-a', '/dev/stderr'], |
||
| 117 | stdin=subprocess.PIPE, stderr=target_fd, stdout=2) |
||
| 118 | except (FileNotFoundError, OSError): |
||
| 119 | tee_stdout = subprocess.Popen( |
||
| 120 | [sys.executable, "-m", "sacred.pytee"], |
||
| 121 | stdin=subprocess.PIPE, stderr=target_fd) |
||
| 122 | tee_stderr = subprocess.Popen( |
||
| 123 | [sys.executable, "-m", "sacred.pytee"], |
||
| 124 | stdin=subprocess.PIPE, stdout=target_fd) |
||
| 125 | |||
| 126 | flush() |
||
| 127 | os.dup2(tee_stdout.stdin.fileno(), original_stdout_fd) |
||
| 128 | os.dup2(tee_stderr.stdin.fileno(), original_stderr_fd) |
||
| 129 | |||
| 130 | try: |
||
| 131 | yield target, final_output # let the caller do their printing |
||
| 132 | finally: |
||
| 133 | flush() |
||
| 134 | |||
| 135 | # then redirect stdout back to the saved fd |
||
| 136 | tee_stdout.stdin.close() |
||
| 137 | tee_stderr.stdin.close() |
||
| 138 | |||
| 139 | # restore original fds |
||
| 140 | os.dup2(saved_stdout_fd, original_stdout_fd) |
||
| 141 | os.dup2(saved_stderr_fd, original_stderr_fd) |
||
| 142 | |||
| 143 | # wait for completion of the tee processes with timeout |
||
| 144 | # implemented using a timer because timeout support is py3 only |
||
| 145 | def kill_tees(): |
||
| 146 | tee_stdout.kill() |
||
| 147 | tee_stderr.kill() |
||
| 148 | |||
| 149 | tee_timer = Timer(1, kill_tees) |
||
| 150 | try: |
||
| 151 | tee_timer.start() |
||
| 152 | tee_stdout.wait() |
||
| 153 | tee_stderr.wait() |
||
| 154 | finally: |
||
| 155 | tee_timer.cancel() |
||
| 156 | |||
| 157 | os.close(saved_stdout_fd) |
||
| 158 | os.close(saved_stderr_fd) |
||
| 159 | target.flush() |
||
| 160 | target.seek(0) |
||
| 161 | final_output.append(target.read().decode()) |
||
| 162 |