| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f4a49ff commit 36cdfd3
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,9 @@ install: | |||
| 25 | 25 | - git config --global user.email "travis@ci.com" | |
| 26 | 26 | - git config --global user.name "Travis Runner" | |
| 27 | 27 | script: | |
| 28 | + # Make sure we limit open handles to see if we are leaking them | ||
| 29 | + - ulimit -n 64 | ||
| 30 | + - ulimit -n | ||
| 28 | 31 | - nosetests -v --with-coverage | |
| 29 | 32 | - flake8 | |
| 30 | 33 | after_success: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,6 +38,9 @@ | |||
| 38 | 38 | ||
| 39 | 39 | __all__ = ('Git', ) | |
| 40 | 40 | ||
| 41 | + if sys.platform != 'win32': | ||
| 42 | + WindowsError = OSError | ||
| 43 | + | ||
| 41 | 44 | ||
| 42 | 45 | # ============================================================================== | |
| 43 | 46 | ## @name Utilities | |
@@ -203,11 +206,18 @@ def __init__(self, proc, args): | |||
| 203 | 206 | self.args = args | |
| 204 | 207 | ||
| 205 | 208 | def __del__(self): | |
| 206 | - self.proc.stdout.close() | ||
| 207 | - self.proc.stderr.close() | ||
| 209 | + if self.proc is None: | ||
| 210 | + return | ||
| 211 | + | ||
| 212 | + proc = self.proc | ||
| 213 | + self.proc = None | ||
| 214 | + if proc.stdin: | ||
| 215 | + proc.stdin.close() | ||
| 216 | + proc.stdout.close() | ||
| 217 | + proc.stderr.close() | ||
| 208 | 218 | ||
| 209 | 219 | # did the process finish already so we have a return code ? | |
| 210 | - if self.proc.poll() is not None: | ||
| 220 | + if proc.poll() is not None: | ||
| 211 | 221 | return | |
| 212 | 222 | ||
| 213 | 223 | # can be that nothing really exists anymore ... | |
@@ -216,16 +226,16 @@ def __del__(self): | |||
| 216 | 226 | ||
| 217 | 227 | # try to kill it | |
| 218 | 228 | try: | |
| 219 | - os.kill(self.proc.pid, 2) # interrupt signal | ||
| 220 | - self.proc.wait() # ensure process goes away | ||
| 229 | + os.kill(proc.pid, 2) # interrupt signal | ||
| 230 | + proc.wait() # ensure process goes away | ||
| 221 | 231 | except (OSError, WindowsError): | |
| 222 | 232 | pass # ignore error when process already died | |
| 223 | 233 | except AttributeError: | |
| 224 | 234 | # try windows | |
| 225 | 235 | # for some reason, providing None for stdout/stderr still prints something. This is why | |
| 226 | 236 | # we simply use the shell and redirect to nul. Its slower than CreateProcess, question | |
| 227 | 237 | # is whether we really want to see all these messages. Its annoying no matter what. | |
| 228 | - call(("TASKKILL /F /T /PID %s 2>nul 1>nul" % str(self.proc.pid)), shell=True) | ||
| 238 | + call(("TASKKILL /F /T /PID %s 2>nul 1>nul" % str(proc.pid)), shell=True) | ||
| 229 | 239 | # END exception handling | |
| 230 | 240 | ||
| 231 | 241 | def __getattr__(self, attr): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -204,6 +204,7 @@ def iter_entries(cls, stream): | |||
| 204 | 204 | return | |
| 205 | 205 | yield new_entry(line.strip()) | |
| 206 | 206 | # END endless loop | |
| 207 | + stream.close() | ||
| 207 | 208 | ||
| 208 | 209 | @classmethod | |
| 209 | 210 | def entry_at(cls, filepath, index): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -168,6 +168,10 @@ def __init__(self, path=None, odbt=DefaultDBType): | |||
| 168 | 168 | args.append(self.git) | |
| 169 | 169 | self.odb = odbt(*args) | |
| 170 | 170 | ||
| 171 | + def __del__(self): | ||
| 172 | + if self.git: | ||
| 173 | + self.git.clear_cache() | ||
| 174 | + | ||
| 171 | 175 | def __eq__(self, rhs): | |
| 172 | 176 | if isinstance(rhs, Repo): | |
| 173 | 177 | return self.git_dir == rhs.git_dir | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -279,6 +279,11 @@ def setUpClass(cls): | |||
| 279 | 279 | """ | |
| 280 | 280 | cls.rorepo = Repo(GIT_REPO) | |
| 281 | 281 | ||
| 282 | + @classmethod | ||
| 283 | + def tearDownClass(cls): | ||
| 284 | + cls.rorepo.git.clear_cache() | ||
| 285 | + cls.rorepo.git = None | ||
| 286 | + | ||
| 282 | 287 | def _make_file(self, rela_path, data, repo=None): | |
| 283 | 288 | """ | |
| 284 | 289 | Create a file at the given path relative to our repository, filled | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -59,6 +59,12 @@ def setUp(self): | |||
| 59 | 59 | self.gitrorepo = Repo(repo_path, odbt=GitCmdObjectDB) | |
| 60 | 60 | self.puregitrorepo = Repo(repo_path, odbt=GitDB) | |
| 61 | 61 | ||
| 62 | + def tearDown(self): | ||
| 63 | + self.gitrorepo.git.clear_cache() | ||
| 64 | + self.gitrorepo = None | ||
| 65 | + self.puregitrorepo.git.clear_cache() | ||
| 66 | + self.puregitrorepo = None | ||
| 67 | + | ||
| 62 | 68 | ||
| 63 | 69 | class TestBigRepoRW(TestBigRepoR): | |
| 64 | 70 | ||
@@ -78,7 +84,12 @@ def setUp(self): | |||
| 78 | 84 | self.puregitrwrepo = Repo(dirname, odbt=GitDB) | |
| 79 | 85 | ||
| 80 | 86 | def tearDown(self): | |
| 87 | + super(TestBigRepoRW, self).tearDown() | ||
| 81 | 88 | if self.gitrwrepo is not None: | |
| 82 | 89 | shutil.rmtree(self.gitrwrepo.working_dir) | |
| 90 | + self.gitrwrepo.git.clear_cache() | ||
| 91 | + self.gitrwrepo = None | ||
| 92 | + self.puregitrwrepo.git.clear_cache() | ||
| 93 | + self.puregitrwrepo = None | ||
| 83 | 94 | ||
| 84 | 95 | #} END base classes | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -656,3 +656,20 @@ def test_git_file(self, rwrepo): | |||
| 656 | 656 | open(git_file_path, 'wb').write(('gitdir: %s\n' % real_path_abs).encode('ascii')) | |
| 657 | 657 | git_file_repo = Repo(rwrepo.working_tree_dir) | |
| 658 | 658 | assert os.path.abspath(git_file_repo.git_dir) == real_path_abs | |
| 659 | + | ||
| 660 | + def test_file_handle_leaks(self): | ||
| 661 | + def last_commit(repo, rev, path): | ||
| 662 | + commit = next(repo.iter_commits(rev, path, max_count=1)) | ||
| 663 | + commit.tree[path] | ||
| 664 | + | ||
| 665 | + # This is based on this comment | ||
| 666 | + # https://github.com/gitpython-developers/GitPython/issues/60#issuecomment-23558741 | ||
| 667 | + # And we expect to set max handles to a low value, like 64 | ||
| 668 | + # You should set ulimit -n X, see .travis.yml | ||
| 669 | + # The loops below would easily create 500 handles if these would leak (4 pipes + multiple mapped files) | ||
| 670 | + for i in range(64): | ||
| 671 | + for repo_type in (GitCmdObjectDB, GitDB): | ||
| 672 | + repo = Repo(self.rorepo.working_tree_dir, odbt=repo_type) | ||
| 673 | + last_commit(repo, 'master', 'git/test/test_base.py') | ||
| 674 | + # end for each repository type | ||
| 675 | + # end for each iteration | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments