From 45d1cd59d39227ee6841042eab85116a59a26d22 Mon Sep 17 00:00:00 2001 From: Bert Wesarg Date: Thu, 11 Mar 2021 16:35:24 +0100 Subject: Remove support for Python 3.5 --- .appveyor.yml | 29 ++--------------------------- .github/workflows/pythonpackage.yml | 4 ++-- .travis.yml | 4 +--- README.md | 2 +- doc/source/intro.rst | 2 +- git/cmd.py | 9 --------- setup.py | 4 ++-- 7 files changed, 9 insertions(+), 45 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 0a86c1a7..833f5c7b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,29 +6,12 @@ environment: CYGWIN64_GIT_PATH: "C:\\cygwin64\\bin;%GIT_DAEMON_PATH%" matrix: - - PYTHON: "C:\\Python34-x64" - PYTHON_VERSION: "3.4" - GIT_PATH: "%GIT_DAEMON_PATH%" - - PYTHON: "C:\\Python35-x64" - PYTHON_VERSION: "3.5" - GIT_PATH: "%GIT_DAEMON_PATH%" - PYTHON: "C:\\Python36-x64" PYTHON_VERSION: "3.6" GIT_PATH: "%GIT_DAEMON_PATH%" - PYTHON: "C:\\Python37-x64" PYTHON_VERSION: "3.7" GIT_PATH: "%GIT_DAEMON_PATH%" - - PYTHON: "C:\\Miniconda35-x64" - PYTHON_VERSION: "3.5" - IS_CONDA: "yes" - MAYFAIL: "yes" - GIT_PATH: "%GIT_DAEMON_PATH%" - ## Cygwin - - PYTHON: "C:\\Python35-x64" - PYTHON_VERSION: "3.5" - IS_CYGWIN: "yes" - MAYFAIL: "yes" - GIT_PATH: "%CYGWIN64_GIT_PATH%" matrix: allow_failures: @@ -76,18 +59,10 @@ install: build: false test_script: - - IF "%IS_CYGWIN%" == "yes" ( - nosetests -v - ) ELSE ( - IF "%PYTHON_VERSION%" == "3.5" ( - nosetests -v --with-coverage - ) ELSE ( - nosetests -v - ) - ) + - nosetests -v on_success: - - IF "%PYTHON_VERSION%" == "3.5" IF NOT "%IS_CYGWIN%" == "yes" (codecov) + - IF "%PYTHON_VERSION%" == "3.6" IF NOT "%IS_CYGWIN%" == "yes" (codecov) # Enable this to be able to login to the build worker. You can use the # `remmina` program in Ubuntu, use the login information that the line below diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 5e94cd05..618d6b97 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 @@ -56,4 +56,4 @@ jobs: run: | set -x pip install -r doc/requirements.txt - make -C doc html \ No newline at end of file + make -C doc html diff --git a/.travis.yml b/.travis.yml index 1fbb1ddb..570beaad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ # UNUSED, only for reference. If adjustments are needed, please see github actions language: python python: - - "3.4" - - "3.5" - "3.6" - "3.7" - "3.8" @@ -38,7 +36,7 @@ script: - ulimit -n - coverage run --omit="test/*" -m unittest --buffer - coverage report - - if [ "$TRAVIS_PYTHON_VERSION" == '3.5' ]; then cd doc && make html; fi + - if [ "$TRAVIS_PYTHON_VERSION" == '3.6' ]; then cd doc && make html; fi - if [ "$TRAVIS_PYTHON_VERSION" == '3.6' ]; then flake8 --ignore=W293,E265,E266,W503,W504,E731; fi after_success: - codecov diff --git a/README.md b/README.md index 0d0edeb4..4725d3ae 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If it is not in your `PATH`, you can help GitPython find it by setting the `GIT_PYTHON_GIT_EXECUTABLE=` environment variable. * Git (1.7.x or newer) -* Python >= 3.5 +* Python >= 3.6 The list of dependencies are listed in `./requirements.txt` and `./test-requirements.txt`. The installer takes care of installing them for you. diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 7168c91b..956a3607 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -13,7 +13,7 @@ The object database implementation is optimized for handling large quantities of Requirements ============ -* `Python`_ >= 3.5 +* `Python`_ >= 3.6 * `Git`_ 1.7.0 or newer It should also work with older versions, but it may be that some operations involving remotes will not work as expected. diff --git a/git/cmd.py b/git/cmd.py index ec630d93..72ec0381 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -17,9 +17,7 @@ from subprocess import ( import subprocess import sys import threading -from collections import OrderedDict from textwrap import dedent -import warnings from git.compat import ( defenc, @@ -903,13 +901,6 @@ class Git(LazyMixin): def transform_kwargs(self, split_single_char_options=True, **kwargs): """Transforms Python style kwargs into git command line options.""" - # Python 3.6 preserves the order of kwargs and thus has a stable - # order. For older versions sort the kwargs by the key to get a stable - # order. - if sys.version_info[:2] < (3, 6): - kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0])) - warnings.warn("Python 3.5 support is deprecated and will be removed 2021-09-05.\n" + - "It does not preserve the order for key-word arguments and enforce lexical sorting instead.") args = [] for k, v in kwargs.items(): if isinstance(v, (list, tuple)): diff --git a/setup.py b/setup.py index f8829c38..850d680d 100755 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ setup( include_package_data=True, py_modules=build_py_modules("./git", excludes=["git.ext.*"]), package_dir={'git': 'git'}, - python_requires='>=3.5', + python_requires='>=3.6', install_requires=requirements, tests_require=requirements + test_requirements, zip_safe=False, @@ -127,6 +127,6 @@ setup( "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9" + "Programming Language :: Python :: 3.9" ] ) -- cgit v1.2.1 From 47f35d1ba2b9b75a9078592cf4c41728ac088793 Mon Sep 17 00:00:00 2001 From: mxrch Date: Fri, 21 May 2021 14:17:07 +0200 Subject: fixed case where progress was no longer shown if a single error occured --- git/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/util.py b/git/util.py index 76aaee49..edbd5f1e 100644 --- a/git/util.py +++ b/git/util.py @@ -470,7 +470,7 @@ class RemoteProgress(object): line_str = line self._cur_line = line_str - if self.error_lines or self._cur_line.startswith(('error:', 'fatal:')): + if self._cur_line.startswith(('error:', 'fatal:')): self.error_lines.append(self._cur_line) return -- cgit v1.2.1 From 1a04c15b1f77f908b1dd3983a27ee49c41b3a3e5 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Mon, 24 May 2021 17:34:42 -0400 Subject: improve index mode for files with executable bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fix for #430 in bebc4f56 (Use correct mode for executable files, 2016-05-19) is incomplete. It fails (in most cases) when files have modes which are not exactly 0644 or 0755. Git only cares whether the executable bit is set (or not). Ensure the mode we set for the index is either 100644 or 100755 based on whether the executable bit is set for the file owner. Do this similarly to how upstream git does it in cache.h¹. Add a test covering various file modes to help catch regressions. Fixes #1253 ¹ https://github.com/git/git/blob/v2.31.1/cache.h#L247 --- git/index/fun.py | 3 ++- test/test_fun.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/git/index/fun.py b/git/index/fun.py index f40928c3..1012f480 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -11,6 +11,7 @@ from stat import ( S_ISDIR, S_IFMT, S_IFREG, + S_IXUSR, ) import subprocess @@ -115,7 +116,7 @@ def stat_mode_to_index_mode(mode): return S_IFLNK if S_ISDIR(mode) or S_IFMT(mode) == S_IFGITLINK: # submodules return S_IFGITLINK - return S_IFREG | 0o644 | (mode & 0o111) # blobs with or without executable bit + return S_IFREG | (mode & S_IXUSR and 0o755 or 0o644) # blobs with or without executable bit def write_cache(entries: Sequence[Union[BaseIndexEntry, 'IndexEntry']], stream: IO[bytes], diff --git a/test/test_fun.py b/test/test_fun.py index a7fb8f8b..e3d07194 100644 --- a/test/test_fun.py +++ b/test/test_fun.py @@ -1,5 +1,5 @@ from io import BytesIO -from stat import S_IFDIR, S_IFREG, S_IFLNK +from stat import S_IFDIR, S_IFREG, S_IFLNK, S_IXUSR from os import stat import os.path as osp from unittest import SkipTest @@ -7,7 +7,8 @@ from unittest import SkipTest from git import Git from git.index import IndexFile from git.index.fun import ( - aggressive_tree_merge + aggressive_tree_merge, + stat_mode_to_index_mode, ) from git.objects.fun import ( traverse_tree_recursive, @@ -206,6 +207,16 @@ class TestFun(TestBase): assert_entries(aggressive_tree_merge(odb, trees), 2, True) # END handle ours, theirs + def test_stat_mode_to_index_mode(self): + modes = ( + 0o600, 0o611, 0o640, 0o641, 0o644, 0o650, 0o651, + 0o700, 0o711, 0o740, 0o744, 0o750, 0o751, 0o755, + ) + for mode in modes: + expected_mode = S_IFREG | (mode & S_IXUSR and 0o755 or 0o644) + assert stat_mode_to_index_mode(mode) == expected_mode + # END for each mode + def _assert_tree_entries(self, entries, num_trees): for entry in entries: assert len(entry) == num_trees -- cgit v1.2.1 From 4fbf0ef97d6f59d2eb0f37b29716ba0de95c4457 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 3 Jun 2021 09:11:18 +0800 Subject: Don't raise on unknown line when parsing stale refs (#1262) --- git/remote.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/remote.py b/git/remote.py index e17f7bb8..6ea4b2a1 100644 --- a/git/remote.py +++ b/git/remote.py @@ -612,7 +612,7 @@ class Remote(LazyMixin, Iterable): # * [would prune] origin/new_branch token = " * [would prune] " if not line.startswith(token): - raise ValueError("Could not parse git-remote prune result: %r" % line) + continue ref_name = line.replace(token, "") # sometimes, paths start with a full ref name, like refs/tags/foo, see #260 if ref_name.startswith(Reference._common_path_default + '/'): -- cgit v1.2.1 From 2dbc2be846d1d00e907efbf8171c35b889ab0155 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Thu, 3 Jun 2021 09:45:09 +0200 Subject: Adds failing test for repo.tag() method --- test/test_repo.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/test/test_repo.py b/test/test_repo.py index 8dc17833..9261f1cf 100644 --- a/test/test_repo.py +++ b/test/test_repo.py @@ -414,6 +414,16 @@ class TestRepo(TestBase): def test_tag(self): assert self.rorepo.tag('refs/tags/0.1.5').commit + def test_tag_to_full_tag_path(self): + tags = ['0.1.5', 'tags/0.1.5', 'refs/tags/0.1.5'] + value_errors = [] + for tag in tags: + try: + self.rorepo.tag(tag) + except ValueError as valueError: + value_errors.append(valueError.args[0]) + raise ValueError('. '.join(value_errors)) + def test_archive(self): tmpfile = tempfile.mktemp(suffix='archive-test') with open(tmpfile, 'wb') as stream: @@ -445,7 +455,7 @@ class TestRepo(TestBase): tlist = b[0][1] self.assertTrue(tlist) self.assertTrue(isinstance(tlist[0], str)) - self.assertTrue(len(tlist) < sum(len(t) for t in tlist)) # test for single-char bug + self.assertTrue(len(tlist) < sum(len(t) for t in tlist)) # test for single-char bug # BINARY BLAME git.return_value = fixture('blame_binary') @@ -454,7 +464,7 @@ class TestRepo(TestBase): def test_blame_real(self): c = 0 - nml = 0 # amount of multi-lines per blame + nml = 0 # amount of multi-lines per blame for item in self.rorepo.head.commit.tree.traverse( predicate=lambda i, d: i.type == 'blob' and i.path.endswith('.py')): c += 1 @@ -486,7 +496,8 @@ class TestRepo(TestBase): # Original line numbers orig_ranges = flatten([entry.orig_linenos for entry in blame_output]) - self.assertEqual(orig_ranges, flatten([range(2, 3), range(14, 15), range(1, 2), range(2, 13), range(13, 15)])) # noqa E501 + self.assertEqual(orig_ranges, flatten( + [range(2, 3), range(14, 15), range(1, 2), range(2, 13), range(13, 15)])) # noqa E501 @mock.patch.object(Git, '_call_process') def test_blame_complex_revision(self, git): @@ -530,9 +541,9 @@ class TestRepo(TestBase): # end for each run def test_config_reader(self): - reader = self.rorepo.config_reader() # all config files + reader = self.rorepo.config_reader() # all config files assert reader.read_only - reader = self.rorepo.config_reader("repository") # single config file + reader = self.rorepo.config_reader("repository") # single config file assert reader.read_only def test_config_writer(self): @@ -729,7 +740,7 @@ class TestRepo(TestBase): def test_rw_rev_parse(self, rwrepo): # verify it does not confuse branches with hexsha ids ahead = rwrepo.create_head('aaaaaaaa') - assert(rwrepo.rev_parse(str(ahead)) == ahead.commit) + assert (rwrepo.rev_parse(str(ahead)) == ahead.commit) def test_rev_parse(self): rev_parse = self.rorepo.rev_parse @@ -1041,7 +1052,7 @@ class TestRepo(TestBase): def test_rebasing(self, rw_dir): r = Repo.init(rw_dir) fp = osp.join(rw_dir, 'hello.txt') - r.git.commit("--allow-empty", message="init",) + r.git.commit("--allow-empty", message="init", ) with open(fp, 'w') as fs: fs.write("hello world") r.git.add(Git.polish_url(fp)) -- cgit v1.2.1 From a625d08801eacd94f373074d2c771103823954d0 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Thu, 3 Jun 2021 10:12:30 +0200 Subject: Adds _common_default to build _common_path_default --- git/refs/tag.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git/refs/tag.py b/git/refs/tag.py index 8f88c522..4d84239e 100644 --- a/git/refs/tag.py +++ b/git/refs/tag.py @@ -18,7 +18,8 @@ class TagReference(Reference): print(tagref.tag.message)""" __slots__ = () - _common_path_default = "refs/tags" + _common_default = "tags" + _common_path_default = Reference._common_path_default + "/" + _common_default @property def commit(self): -- cgit v1.2.1 From 057514e85bc99754e08d45385bf316920963adf9 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Thu, 3 Jun 2021 10:18:46 +0200 Subject: Fixes test to not throw false negative results --- test/test_repo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_repo.py b/test/test_repo.py index 9261f1cf..453ec5c3 100644 --- a/test/test_repo.py +++ b/test/test_repo.py @@ -422,7 +422,8 @@ class TestRepo(TestBase): self.rorepo.tag(tag) except ValueError as valueError: value_errors.append(valueError.args[0]) - raise ValueError('. '.join(value_errors)) + if value_errors: + raise ValueError('. '.join(value_errors)) def test_archive(self): tmpfile = tempfile.mktemp(suffix='archive-test') -- cgit v1.2.1 From abf9373865c319d2f1aaf188feef900bb8ebf933 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Thu, 3 Jun 2021 10:21:50 +0200 Subject: Fixes resolving of tag parameter for repo.tag I accessed private variables instead of adding getters, because other parts of the code do the same and I didn't know if there was a reason for it. E.g.: remote.py line 409: (...) RemoteReference._common_path_default (...) --- git/repo/base.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/git/repo/base.py b/git/repo/base.py index e23ebb1a..540a5fe3 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -402,7 +402,18 @@ class Repo(object): def tag(self, path: PathLike) -> TagReference: """:return: TagReference Object, reference pointing to a Commit or Tag :param path: path to the tag reference, i.e. 0.1.5 or tags/0.1.5 """ - return TagReference(self, path) + full_path = self._to_full_tag_path(path) + return TagReference(self, full_path) + + @staticmethod + def _to_full_tag_path(path: PathLike): + if path.startswith(TagReference._common_path_default + '/'): + return path + if path.startswith(TagReference._common_default + '/'): + return Reference._common_path_default + '/' + path + else: + return TagReference._common_path_default + '/' + path + def create_head(self, path: PathLike, commit: str = 'HEAD', force: bool = False, logmsg: Optional[str] = None -- cgit v1.2.1 From 79e24f78fa35136216130a10d163c91f9a6d4970 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Thu, 3 Jun 2021 10:52:59 +0200 Subject: Reverts auto format introduced with 2dbc2be8 --- test/test_repo.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/test/test_repo.py b/test/test_repo.py index 453ec5c3..0311653a 100644 --- a/test/test_repo.py +++ b/test/test_repo.py @@ -422,8 +422,7 @@ class TestRepo(TestBase): self.rorepo.tag(tag) except ValueError as valueError: value_errors.append(valueError.args[0]) - if value_errors: - raise ValueError('. '.join(value_errors)) + raise ValueError('. '.join(value_errors)) def test_archive(self): tmpfile = tempfile.mktemp(suffix='archive-test') @@ -456,7 +455,7 @@ class TestRepo(TestBase): tlist = b[0][1] self.assertTrue(tlist) self.assertTrue(isinstance(tlist[0], str)) - self.assertTrue(len(tlist) < sum(len(t) for t in tlist)) # test for single-char bug + self.assertTrue(len(tlist) < sum(len(t) for t in tlist)) # test for single-char bug # BINARY BLAME git.return_value = fixture('blame_binary') @@ -465,7 +464,7 @@ class TestRepo(TestBase): def test_blame_real(self): c = 0 - nml = 0 # amount of multi-lines per blame + nml = 0 # amount of multi-lines per blame for item in self.rorepo.head.commit.tree.traverse( predicate=lambda i, d: i.type == 'blob' and i.path.endswith('.py')): c += 1 @@ -497,8 +496,7 @@ class TestRepo(TestBase): # Original line numbers orig_ranges = flatten([entry.orig_linenos for entry in blame_output]) - self.assertEqual(orig_ranges, flatten( - [range(2, 3), range(14, 15), range(1, 2), range(2, 13), range(13, 15)])) # noqa E501 + self.assertEqual(orig_ranges, flatten([range(2, 3), range(14, 15), range(1, 2), range(2, 13), range(13, 15)])) # noqa E501 @mock.patch.object(Git, '_call_process') def test_blame_complex_revision(self, git): @@ -542,9 +540,9 @@ class TestRepo(TestBase): # end for each run def test_config_reader(self): - reader = self.rorepo.config_reader() # all config files + reader = self.rorepo.config_reader() # all config files assert reader.read_only - reader = self.rorepo.config_reader("repository") # single config file + reader = self.rorepo.config_reader("repository") # single config file assert reader.read_only def test_config_writer(self): @@ -741,7 +739,7 @@ class TestRepo(TestBase): def test_rw_rev_parse(self, rwrepo): # verify it does not confuse branches with hexsha ids ahead = rwrepo.create_head('aaaaaaaa') - assert (rwrepo.rev_parse(str(ahead)) == ahead.commit) + assert(rwrepo.rev_parse(str(ahead)) == ahead.commit) def test_rev_parse(self): rev_parse = self.rorepo.rev_parse @@ -1053,7 +1051,7 @@ class TestRepo(TestBase): def test_rebasing(self, rw_dir): r = Repo.init(rw_dir) fp = osp.join(rw_dir, 'hello.txt') - r.git.commit("--allow-empty", message="init", ) + r.git.commit("--allow-empty", message="init",) with open(fp, 'w') as fs: fs.write("hello world") r.git.add(Git.polish_url(fp)) -- cgit v1.2.1 From 5a61a63ed4bb866b2817acbb04e045f8460e040e Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Thu, 3 Jun 2021 11:10:39 +0200 Subject: Adds name to AUTHORS file --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 7b21b2b2..606796d9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -43,4 +43,5 @@ Contributors are: -Liam Beguin -Ram Rachum -Alba Mendez +-Robert Westman Portions derived from other open source works and are clearly marked. -- cgit v1.2.1 From 702bdf105205ca845a50b16d6703828d18e93003 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 3 Jun 2021 21:06:30 +0800 Subject: Fix flake8 --- git/repo/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/git/repo/base.py b/git/repo/base.py index 540a5fe3..53698592 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -414,7 +414,6 @@ class Repo(object): else: return TagReference._common_path_default + '/' + path - def create_head(self, path: PathLike, commit: str = 'HEAD', force: bool = False, logmsg: Optional[str] = None ) -> 'SymbolicReference': -- cgit v1.2.1 From 7ca97dcef3131a11dd5ef41d674bb6bd36608608 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Thu, 3 Jun 2021 16:45:03 +0200 Subject: Removes PathLike type requirement for full_tag creation --- git/repo/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/repo/base.py b/git/repo/base.py index 53698592..55682411 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -406,7 +406,7 @@ class Repo(object): return TagReference(self, full_path) @staticmethod - def _to_full_tag_path(path: PathLike): + def _to_full_tag_path(path): if path.startswith(TagReference._common_path_default + '/'): return path if path.startswith(TagReference._common_default + '/'): -- cgit v1.2.1 From 01a96b92f7d873cbd531d142813c2be7ab88d5a5 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 4 Jun 2021 10:35:22 +0800 Subject: Conditionally throw an error --- test/test_repo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_repo.py b/test/test_repo.py index 0311653a..04102b01 100644 --- a/test/test_repo.py +++ b/test/test_repo.py @@ -422,7 +422,7 @@ class TestRepo(TestBase): self.rorepo.tag(tag) except ValueError as valueError: value_errors.append(valueError.args[0]) - raise ValueError('. '.join(value_errors)) + self.assertEqual(value_errors, []) def test_archive(self): tmpfile = tempfile.mktemp(suffix='archive-test') -- cgit v1.2.1 From 385a8c6c1a72dc34f69c5273c1b4c1285cc1d3c5 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Sat, 5 Jun 2021 12:15:38 +0200 Subject: Adds repo.is_valid_object check --- git/repo/base.py | 21 ++++++++++++++++++++- test/test_repo.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/git/repo/base.py b/git/repo/base.py index 55682411..e7b1274b 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -3,12 +3,14 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php - +import binascii import logging import os import re import warnings +from gitdb.exc import BadObject + from git.cmd import ( Git, handle_process_output @@ -618,6 +620,23 @@ class Repo(object): raise return True + def is_valid_object(self, sha: str, object_type: Union['blob', 'commit', 'tree', 'tag'] = None) -> bool: + try: + complete_sha = self.odb.partial_to_complete_sha_hex(sha) + object_info = self.odb.info(complete_sha) + if object_type: + if object_info.type == object_type.encode(): + return True + else: + log.debug(f"Commit hash points to an object of type '{object_info.type.decode()}'. " + f"Requested were objects of type '{object_type}'") + return False + else: + return True + except BadObject as e: + log.debug("Commit hash is invalid.") + return False + def _get_daemon_export(self) -> bool: if self.git_dir: filename = osp.join(self.git_dir, self.DAEMON_EXPORT_FILE) diff --git a/test/test_repo.py b/test/test_repo.py index 04102b01..8aced94d 100644 --- a/test/test_repo.py +++ b/test/test_repo.py @@ -989,6 +989,34 @@ class TestRepo(TestBase): for i, j in itertools.permutations([c1, 'ffffff', ''], r=2): self.assertRaises(GitCommandError, repo.is_ancestor, i, j) + def test_is_valid_object(self): + repo = self.rorepo + commit_sha = 'f6aa8d1' + blob_sha = '1fbe3e4375' + tree_sha = '960b40fe36' + tag_sha = '42c2f60c43' + + # Check for valid objects + self.assertTrue(repo.is_valid_object(commit_sha)) + self.assertTrue(repo.is_valid_object(blob_sha)) + self.assertTrue(repo.is_valid_object(tree_sha)) + self.assertTrue(repo.is_valid_object(tag_sha)) + + # Check for valid objects of specific type + self.assertTrue(repo.is_valid_object(commit_sha, 'commit')) + self.assertTrue(repo.is_valid_object(blob_sha, 'blob')) + self.assertTrue(repo.is_valid_object(tree_sha, 'tree')) + self.assertTrue(repo.is_valid_object(tag_sha, 'tag')) + + # Check for invalid objects + self.assertFalse(repo.is_valid_object(b'1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a', 'blob')) + + # Check for invalid objects of specific type + self.assertFalse(repo.is_valid_object(commit_sha, 'blob')) + self.assertFalse(repo.is_valid_object(blob_sha, 'commit')) + self.assertFalse(repo.is_valid_object(tree_sha, 'commit')) + self.assertFalse(repo.is_valid_object(tag_sha, 'commit')) + @with_rw_directory def test_git_work_tree_dotgit(self, rw_dir): """Check that we find .git as a worktree file and find the worktree -- cgit v1.2.1 From ac4fe6efbccc2ad5c2044bf36e34019363018630 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Sat, 5 Jun 2021 12:22:24 +0200 Subject: Fixes type check for is_valid_object --- git/repo/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/repo/base.py b/git/repo/base.py index e7b1274b..b19503ee 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -620,7 +620,7 @@ class Repo(object): raise return True - def is_valid_object(self, sha: str, object_type: Union['blob', 'commit', 'tree', 'tag'] = None) -> bool: + def is_valid_object(self, sha: str, object_type: str = None) -> bool: try: complete_sha = self.odb.partial_to_complete_sha_hex(sha) object_info = self.odb.info(complete_sha) -- cgit v1.2.1 From 4832aa6bf82e4853f8f426fc06350540e2c8a9e7 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Sat, 5 Jun 2021 12:34:24 +0200 Subject: Removes local variable 'e' that is assigned to but never used --- git/repo/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/repo/base.py b/git/repo/base.py index b19503ee..d38cf756 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -633,7 +633,7 @@ class Repo(object): return False else: return True - except BadObject as e: + except BadObject: log.debug("Commit hash is invalid.") return False -- cgit v1.2.1 From fb2461d84f97a72641ef1e878450aeab7cd17241 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Sat, 5 Jun 2021 12:35:41 +0200 Subject: Removes unused import --- git/repo/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/git/repo/base.py b/git/repo/base.py index d38cf756..0db0bd0c 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -3,7 +3,6 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -import binascii import logging import os import re -- cgit v1.2.1 From 464504ce0069758fdb88b348e4a626a265fb3fe3 Mon Sep 17 00:00:00 2001 From: Robert Westman Date: Sat, 5 Jun 2021 12:39:44 +0200 Subject: Removes f-string syntax for p35 compatibility --- git/repo/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git/repo/base.py b/git/repo/base.py index 0db0bd0c..6cc56031 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -627,8 +627,8 @@ class Repo(object): if object_info.type == object_type.encode(): return True else: - log.debug(f"Commit hash points to an object of type '{object_info.type.decode()}'. " - f"Requested were objects of type '{object_type}'") + log.debug("Commit hash points to an object of type '%s'. Requested were objects of type '%s'", + object_info.type.decode(), object_type) return False else: return True -- cgit v1.2.1 From 4d86d883714072b6e3bbc56a2127c06e9d6a6582 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 16 Jun 2021 11:03:13 +0800 Subject: prepare patch level 3.1.18 --- VERSION | 2 +- doc/source/changes.rst | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 3797f3f9..5762a6ff 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.1.17 +3.1.18 diff --git a/doc/source/changes.rst b/doc/source/changes.rst index 68a94516..aabef802 100644 --- a/doc/source/changes.rst +++ b/doc/source/changes.rst @@ -2,6 +2,14 @@ Changelog ========= +3.1.18 +====== + +* drop support for python 3.5 to reduce maintenance burden on typing. Lower patch levels of python 3.5 would break, too. + +See the following for details: +https://github.com/gitpython-developers/gitpython/milestone/50?closed=1 + 3.1.17 ====== -- cgit v1.2.1 From 820d3cc9ceda3e5690d627677883b7f9d349b326 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 16 Jun 2021 11:22:27 +0800 Subject: Revert "Remove support for Python 3.5" - fix CI for now. This reverts commit 45d1cd59d39227ee6841042eab85116a59a26d22. See #1201 which will hopefully help to get a proper fix soon. --- .appveyor.yml | 29 +++++++++++++++++++++++++++-- .github/workflows/pythonpackage.yml | 4 ++-- .travis.yml | 4 +++- README.md | 2 +- doc/source/intro.rst | 2 +- git/cmd.py | 9 +++++++++ setup.py | 4 ++-- 7 files changed, 45 insertions(+), 9 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 833f5c7b..0a86c1a7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,12 +6,29 @@ environment: CYGWIN64_GIT_PATH: "C:\\cygwin64\\bin;%GIT_DAEMON_PATH%" matrix: + - PYTHON: "C:\\Python34-x64" + PYTHON_VERSION: "3.4" + GIT_PATH: "%GIT_DAEMON_PATH%" + - PYTHON: "C:\\Python35-x64" + PYTHON_VERSION: "3.5" + GIT_PATH: "%GIT_DAEMON_PATH%" - PYTHON: "C:\\Python36-x64" PYTHON_VERSION: "3.6" GIT_PATH: "%GIT_DAEMON_PATH%" - PYTHON: "C:\\Python37-x64" PYTHON_VERSION: "3.7" GIT_PATH: "%GIT_DAEMON_PATH%" + - PYTHON: "C:\\Miniconda35-x64" + PYTHON_VERSION: "3.5" + IS_CONDA: "yes" + MAYFAIL: "yes" + GIT_PATH: "%GIT_DAEMON_PATH%" + ## Cygwin + - PYTHON: "C:\\Python35-x64" + PYTHON_VERSION: "3.5" + IS_CYGWIN: "yes" + MAYFAIL: "yes" + GIT_PATH: "%CYGWIN64_GIT_PATH%" matrix: allow_failures: @@ -59,10 +76,18 @@ install: build: false test_script: - - nosetests -v + - IF "%IS_CYGWIN%" == "yes" ( + nosetests -v + ) ELSE ( + IF "%PYTHON_VERSION%" == "3.5" ( + nosetests -v --with-coverage + ) ELSE ( + nosetests -v + ) + ) on_success: - - IF "%PYTHON_VERSION%" == "3.6" IF NOT "%IS_CYGWIN%" == "yes" (codecov) + - IF "%PYTHON_VERSION%" == "3.5" IF NOT "%IS_CYGWIN%" == "yes" (codecov) # Enable this to be able to login to the build worker. You can use the # `remmina` program in Ubuntu, use the login information that the line below diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 53da7614..65d5e6cd 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: [3.5, 3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 @@ -61,4 +61,4 @@ jobs: run: | set -x pip install -r doc/requirements.txt - make -C doc html + make -C doc html \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 570beaad..1fbb1ddb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ # UNUSED, only for reference. If adjustments are needed, please see github actions language: python python: + - "3.4" + - "3.5" - "3.6" - "3.7" - "3.8" @@ -36,7 +38,7 @@ script: - ulimit -n - coverage run --omit="test/*" -m unittest --buffer - coverage report - - if [ "$TRAVIS_PYTHON_VERSION" == '3.6' ]; then cd doc && make html; fi + - if [ "$TRAVIS_PYTHON_VERSION" == '3.5' ]; then cd doc && make html; fi - if [ "$TRAVIS_PYTHON_VERSION" == '3.6' ]; then flake8 --ignore=W293,E265,E266,W503,W504,E731; fi after_success: - codecov diff --git a/README.md b/README.md index 4725d3ae..0d0edeb4 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If it is not in your `PATH`, you can help GitPython find it by setting the `GIT_PYTHON_GIT_EXECUTABLE=` environment variable. * Git (1.7.x or newer) -* Python >= 3.6 +* Python >= 3.5 The list of dependencies are listed in `./requirements.txt` and `./test-requirements.txt`. The installer takes care of installing them for you. diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 956a3607..7168c91b 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -13,7 +13,7 @@ The object database implementation is optimized for handling large quantities of Requirements ============ -* `Python`_ >= 3.6 +* `Python`_ >= 3.5 * `Git`_ 1.7.0 or newer It should also work with older versions, but it may be that some operations involving remotes will not work as expected. diff --git a/git/cmd.py b/git/cmd.py index 4f58b314..d8b82352 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -17,7 +17,9 @@ from subprocess import ( import subprocess import sys import threading +from collections import OrderedDict from textwrap import dedent +import warnings from git.compat import ( defenc, @@ -1003,6 +1005,13 @@ class Git(LazyMixin): def transform_kwargs(self, split_single_char_options: bool = True, **kwargs: Any) -> List[str]: """Transforms Python style kwargs into git command line options.""" + # Python 3.6 preserves the order of kwargs and thus has a stable + # order. For older versions sort the kwargs by the key to get a stable + # order. + if sys.version_info[:2] < (3, 6): + kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0])) + warnings.warn("Python 3.5 support is deprecated and will be removed 2021-09-05.\n" + + "It does not preserve the order for key-word arguments and enforce lexical sorting instead.") args = [] for k, v in kwargs.items(): if isinstance(v, (list, tuple)): diff --git a/setup.py b/setup.py index 850d680d..f8829c38 100755 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ setup( include_package_data=True, py_modules=build_py_modules("./git", excludes=["git.ext.*"]), package_dir={'git': 'git'}, - python_requires='>=3.6', + python_requires='>=3.5', install_requires=requirements, tests_require=requirements + test_requirements, zip_safe=False, @@ -127,6 +127,6 @@ setup( "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9" + "Programming Language :: Python :: 3.9" ] ) -- cgit v1.2.1 From b0f79c58ad919e90261d1e332df79a4ad0bc40de Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 16 Jun 2021 11:27:07 +0800 Subject: Revert "Revert "Remove support for Python 3.5" - fix CI for now." This reverts commit 820d3cc9ceda3e5690d627677883b7f9d349b326. --- .appveyor.yml | 29 ++--------------------------- .github/workflows/pythonpackage.yml | 4 ++-- .travis.yml | 4 +--- README.md | 2 +- doc/source/intro.rst | 2 +- git/cmd.py | 9 --------- setup.py | 4 ++-- 7 files changed, 9 insertions(+), 45 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 0a86c1a7..833f5c7b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,29 +6,12 @@ environment: CYGWIN64_GIT_PATH: "C:\\cygwin64\\bin;%GIT_DAEMON_PATH%" matrix: - - PYTHON: "C:\\Python34-x64" - PYTHON_VERSION: "3.4" - GIT_PATH: "%GIT_DAEMON_PATH%" - - PYTHON: "C:\\Python35-x64" - PYTHON_VERSION: "3.5" - GIT_PATH: "%GIT_DAEMON_PATH%" - PYTHON: "C:\\Python36-x64" PYTHON_VERSION: "3.6" GIT_PATH: "%GIT_DAEMON_PATH%" - PYTHON: "C:\\Python37-x64" PYTHON_VERSION: "3.7" GIT_PATH: "%GIT_DAEMON_PATH%" - - PYTHON: "C:\\Miniconda35-x64" - PYTHON_VERSION: "3.5" - IS_CONDA: "yes" - MAYFAIL: "yes" - GIT_PATH: "%GIT_DAEMON_PATH%" - ## Cygwin - - PYTHON: "C:\\Python35-x64" - PYTHON_VERSION: "3.5" - IS_CYGWIN: "yes" - MAYFAIL: "yes" - GIT_PATH: "%CYGWIN64_GIT_PATH%" matrix: allow_failures: @@ -76,18 +59,10 @@ install: build: false test_script: - - IF "%IS_CYGWIN%" == "yes" ( - nosetests -v - ) ELSE ( - IF "%PYTHON_VERSION%" == "3.5" ( - nosetests -v --with-coverage - ) ELSE ( - nosetests -v - ) - ) + - nosetests -v on_success: - - IF "%PYTHON_VERSION%" == "3.5" IF NOT "%IS_CYGWIN%" == "yes" (codecov) + - IF "%PYTHON_VERSION%" == "3.6" IF NOT "%IS_CYGWIN%" == "yes" (codecov) # Enable this to be able to login to the build worker. You can use the # `remmina` program in Ubuntu, use the login information that the line below diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 65d5e6cd..53da7614 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 @@ -61,4 +61,4 @@ jobs: run: | set -x pip install -r doc/requirements.txt - make -C doc html \ No newline at end of file + make -C doc html diff --git a/.travis.yml b/.travis.yml index 1fbb1ddb..570beaad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ # UNUSED, only for reference. If adjustments are needed, please see github actions language: python python: - - "3.4" - - "3.5" - "3.6" - "3.7" - "3.8" @@ -38,7 +36,7 @@ script: - ulimit -n - coverage run --omit="test/*" -m unittest --buffer - coverage report - - if [ "$TRAVIS_PYTHON_VERSION" == '3.5' ]; then cd doc && make html; fi + - if [ "$TRAVIS_PYTHON_VERSION" == '3.6' ]; then cd doc && make html; fi - if [ "$TRAVIS_PYTHON_VERSION" == '3.6' ]; then flake8 --ignore=W293,E265,E266,W503,W504,E731; fi after_success: - codecov diff --git a/README.md b/README.md index 0d0edeb4..4725d3ae 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If it is not in your `PATH`, you can help GitPython find it by setting the `GIT_PYTHON_GIT_EXECUTABLE=` environment variable. * Git (1.7.x or newer) -* Python >= 3.5 +* Python >= 3.6 The list of dependencies are listed in `./requirements.txt` and `./test-requirements.txt`. The installer takes care of installing them for you. diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 7168c91b..956a3607 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -13,7 +13,7 @@ The object database implementation is optimized for handling large quantities of Requirements ============ -* `Python`_ >= 3.5 +* `Python`_ >= 3.6 * `Git`_ 1.7.0 or newer It should also work with older versions, but it may be that some operations involving remotes will not work as expected. diff --git a/git/cmd.py b/git/cmd.py index d8b82352..4f58b314 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -17,9 +17,7 @@ from subprocess import ( import subprocess import sys import threading -from collections import OrderedDict from textwrap import dedent -import warnings from git.compat import ( defenc, @@ -1005,13 +1003,6 @@ class Git(LazyMixin): def transform_kwargs(self, split_single_char_options: bool = True, **kwargs: Any) -> List[str]: """Transforms Python style kwargs into git command line options.""" - # Python 3.6 preserves the order of kwargs and thus has a stable - # order. For older versions sort the kwargs by the key to get a stable - # order. - if sys.version_info[:2] < (3, 6): - kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0])) - warnings.warn("Python 3.5 support is deprecated and will be removed 2021-09-05.\n" + - "It does not preserve the order for key-word arguments and enforce lexical sorting instead.") args = [] for k, v in kwargs.items(): if isinstance(v, (list, tuple)): diff --git a/setup.py b/setup.py index f8829c38..850d680d 100755 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ setup( include_package_data=True, py_modules=build_py_modules("./git", excludes=["git.ext.*"]), package_dir={'git': 'git'}, - python_requires='>=3.5', + python_requires='>=3.6', install_requires=requirements, tests_require=requirements + test_requirements, zip_safe=False, @@ -127,6 +127,6 @@ setup( "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9" + "Programming Language :: Python :: 3.9" ] ) -- cgit v1.2.1