diff --git a/.appveyor.yml b/.appveyor.yml index 5a96f878e..8eeca501c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,9 +7,6 @@ environment: matrix: ## MINGW # - - PYTHON: "C:\\Python26" - PYTHON_VERSION: "2.6" - GIT_PATH: "%GIT_DAEMON_PATH%" - PYTHON: "C:\\Python27" PYTHON_VERSION: "2.7" GIT_PATH: "%GIT_DAEMON_PATH%" diff --git a/.travis.yml b/.travis.yml index f20e76ae9..52223bdb9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ language: python python: - - "2.6" - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" @@ -12,7 +10,6 @@ python: # - "pypy" - won't work as smmap doesn't work (see gitdb/.travis.yml for details) matrix: allow_failures: - - python: "2.6" - python: "3.6-dev" - python: "3.7-dev" - python: "nightly" @@ -26,7 +23,6 @@ install: - git fetch --tags - pip install -r test-requirements.txt - pip install codecov sphinx - - if [ "$TRAVIS_PYTHON_VERSION" == '2.6' ]; then pip install unittest2; fi # generate some reflog as git-python tests need it (in master) - ./init-tests-after-clone.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 89ced5084..3279a6722 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ ### How to contribute -* [fork this project](https://github.com/gitpython-developers/GitPython/fork) on github +* [fork this project](https://github.com/gitpython-developers/GitPython/fork) on GitHub * For setting up the environment to run the self tests, look at `.travis.yml`. * Add yourself to AUTHORS.md and write your patch. **Write a test that fails unless your patch is present.** * Initiate a pull request diff --git a/README.md b/README.md index 33b2028ef..8e9d21265 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,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 2.7 to 3.6, while python 2.6 is supported on a *best-effort basis*. +* Python 2.7 to 3.6. The list of dependencies are listed in `./requirements.txt` and `./test-requirements.txt`. The installer takes care of installing them for you. @@ -68,10 +68,6 @@ For *Windows*, we do regularly test it on [Appveyor CI](https://www.appveyor.com but not all test-cases pass - you may help improve them by exploring [Issue #525](https://github.com/gitpython-developers/GitPython/issues/525). -#### Python 2.6 - -Python 2.6 is supported on best-effort basis; which means that it is likely to deteriorate over time. - ### RUNNING TESTS *Important*: Right after cloning this repository, please be sure to have executed diff --git a/doc/source/changes.rst b/doc/source/changes.rst index 70f66a7d0..129c96ca9 100644 --- a/doc/source/changes.rst +++ b/doc/source/changes.rst @@ -161,13 +161,13 @@ Please note that due to breaking changes, we have to increase the major version. with large repositories. * CRITICAL: fixed incorrect `Commit` object serialization when authored or commit date had timezones which were not divisiblej by 3600 seconds. This would happen if the timezone was something like `+0530` for instance. -* A list of all additional fixes can be found `on github `_ +* A list of all additional fixes can be found `on GitHub `_ * CRITICAL: `Tree.cache` was removed without replacement. It is technically impossible to change individual trees and expect their serialization results to be consistent with what *git* expects. Instead, use the `IndexFile` facilities to adjust the content of the staging area, and write it out to the respective tree objects using `IndexFile.write_tree()` instead. 1.0.1 - Fixes ============= -* A list of all issues can be found `on github `_ +* A list of all issues can be found `on GitHub `_ 1.0.0 - Notes ============= @@ -191,7 +191,7 @@ It follows the `semantic version scheme `_, and thus will not - Those who support **GUI on windows** will now have to set `git.Git.USE_SHELL = True` to get the previous behaviour. -* A list of all issues can be found `on github `_ +* A list of all issues can be found `on GitHub `_ 0.3.6 - Features @@ -207,11 +207,11 @@ It follows the `semantic version scheme `_, and thus will not * Repo.working_tree_dir now returns None if it is bare. Previously it raised AssertionError. * IndexFile.add() previously raised AssertionError when paths where used with bare repository, now it raises InvalidGitRepositoryError -* Added `Repo.merge_base()` implementation. See the `respective issue on github `_ +* Added `Repo.merge_base()` implementation. See the `respective issue on GitHub `_ * `[include]` sections in git configuration files are now respected * Added `GitConfigParser.rename_section()` * Added `Submodule.rename()` -* A list of all issues can be found `on github `_ +* A list of all issues can be found `on GitHub `_ 0.3.5 - Bugfixes ================ diff --git a/doc/source/intro.rst b/doc/source/intro.rst index bfc5a7788..48f898b25 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -14,8 +14,6 @@ Requirements ============ * `Python`_ 2.7 or newer - Since GitPython 2.0.0. Please note that python 2.6 is still reasonably well supported, but might - deteriorate over time. Support is provided on a best-effort basis only. * `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. @@ -75,12 +73,6 @@ codebase for `__del__` implementations and call these yourself when you see fit. Another way assure proper cleanup of resources is to factor out GitPython into a separate process which can be dropped periodically. -Best-effort for Python 2.6 and Windows support ----------------------------------------------- - -This means that support for these platforms is likely to worsen over time -as they are kept alive solely by their users, or not. - Getting Started =============== @@ -124,7 +116,7 @@ http://stackoverflow.com/questions/tagged/gitpython Issue Tracker ============= -The issue tracker is hosted by github: +The issue tracker is hosted by GitHub: https://github.com/gitpython-developers/GitPython/issues diff --git a/doc/source/roadmap.rst b/doc/source/roadmap.rst index f93d5e65b..a573df33a 100644 --- a/doc/source/roadmap.rst +++ b/doc/source/roadmap.rst @@ -2,7 +2,7 @@ ####### Roadmap ####### -The full list of milestones including associated tasks can be found on github: +The full list of milestones including associated tasks can be found on GitHub: https://github.com/gitpython-developers/GitPython/issues Select the respective milestone to filter the list of issues accordingly. diff --git a/git/cmd.py b/git/cmd.py index 13c01401d..0f797e239 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -17,6 +17,7 @@ import subprocess import sys import threading +from collections import OrderedDict from textwrap import dedent from git.compat import ( @@ -31,7 +32,6 @@ is_win, ) from git.exc import CommandError -from git.odict import OrderedDict from git.util import is_cygwin_git, cygpath, expand_path from .exc import ( @@ -44,10 +44,10 @@ ) -execute_kwargs = set(('istream', 'with_extended_output', - 'with_exceptions', 'as_process', 'stdout_as_string', - 'output_stream', 'with_stdout', 'kill_after_timeout', - 'universal_newlines', 'shell', 'env')) +execute_kwargs = {'istream', 'with_extended_output', 'with_exceptions', + 'as_process', 'stdout_as_string', 'output_stream', + 'with_stdout', 'kill_after_timeout', 'universal_newlines', + 'shell', 'env'} log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) @@ -125,7 +125,7 @@ def dashify(string): def slots_to_dict(self, exclude=()): - return dict((s, getattr(self, s)) for s in self.__slots__ if s not in exclude) + return {s: getattr(self, s) for s in self.__slots__ if s not in exclude} def dict_to_slots_and__excluded_are_none(self, d, excluded=()): @@ -143,8 +143,7 @@ def dict_to_slots_and__excluded_are_none(self, d, excluded=()): ## CREATE_NEW_PROCESS_GROUP is needed to allow killing it afterwards, # see https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal PROC_CREATIONFLAGS = (CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP - if is_win and sys.version_info >= (2, 7) - else 0) + if is_win else 0) class Git(LazyMixin): @@ -486,10 +485,10 @@ def readline(self, size=-1): def readlines(self, size=-1): if self._nbr == self._size: - return list() + return [] # leave all additional logic to our readline method, we just check the size - out = list() + out = [] nbr = 0 while True: line = self.readline() @@ -895,7 +894,7 @@ def transform_kwarg(self, name, value, split_single_char_options): def transform_kwargs(self, split_single_char_options=True, **kwargs): """Transforms Python style kwargs into git command line options.""" - args = list() + args = [] kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0])) for k, v in kwargs.items(): if isinstance(v, (list, tuple)): @@ -914,7 +913,7 @@ def __unpack_args(cls, arg_list): return [arg_list.encode(defenc)] return [str(arg_list)] - outlist = list() + outlist = [] for arg in arg_list: if isinstance(arg_list, (list, tuple)): outlist.extend(cls.__unpack_args(arg)) @@ -973,8 +972,8 @@ def _call_process(self, method, *args, **kwargs): :return: Same as ``execute``""" # Handle optional arguments prior to calling transform_kwargs # otherwise these'll end up in args, which is bad. - exec_kwargs = dict((k, v) for k, v in kwargs.items() if k in execute_kwargs) - opts_kwargs = dict((k, v) for k, v in kwargs.items() if k not in execute_kwargs) + exec_kwargs = {k: v for k, v in kwargs.items() if k in execute_kwargs} + opts_kwargs = {k: v for k, v in kwargs.items() if k not in execute_kwargs} insert_after_this_arg = opts_kwargs.pop('insert_kwargs_after', None) diff --git a/git/config.py b/git/config.py index 3310db890..68d65ae91 100644 --- a/git/config.py +++ b/git/config.py @@ -12,6 +12,7 @@ import logging import os import re +from collections import OrderedDict from git.compat import ( string_types, @@ -21,7 +22,6 @@ with_metaclass, PY3 ) -from git.odict import OrderedDict from git.util import LockFile import os.path as osp diff --git a/git/diff.py b/git/diff.py index 28c10e49e..d7221ac7d 100644 --- a/git/diff.py +++ b/git/diff.py @@ -61,7 +61,7 @@ class Diffable(object): :note: Subclasses require a repo member as it is the case for Object instances, for practical reasons we do not derive from Object.""" - __slots__ = tuple() + __slots__ = () # standin indicating you want to diff against the index class Index(object): @@ -106,7 +106,7 @@ def diff(self, other=Index, paths=None, create_patch=False, **kwargs): :note: On a bare repository, 'other' needs to be provided as Index or as as Tree/Commit, or a git command error will occur""" - args = list() + args = [] args.append("--abbrev=40") # we need full shas args.append("--full-index") # get full index paths, not only filenames diff --git a/git/index/base.py b/git/index/base.py index e6682d5d9..14a3117aa 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -119,7 +119,7 @@ def _set_cache_(self, attr): ok = True except OSError: # in new repositories, there may be no index, which means we are empty - self.entries = dict() + self.entries = {} return finally: if not ok: @@ -316,7 +316,7 @@ def from_tree(cls, repo, *treeish, **kwargs): if len(treeish) == 0 or len(treeish) > 3: raise ValueError("Please specify between 1 and 3 treeish, got %i" % len(treeish)) - arg_list = list() + arg_list = [] # ignore that working tree and index possibly are out of date if len(treeish) > 1: # drop unmerged entries when reading our index and merging @@ -463,9 +463,9 @@ def unmerged_blobs(self): are at stage 3 will not have a stage 3 entry. """ is_unmerged_blob = lambda t: t[0] != 0 - path_map = dict() + path_map = {} for stage, blob in self.iter_blobs(is_unmerged_blob): - path_map.setdefault(blob.path, list()).append((stage, blob)) + path_map.setdefault(blob.path, []).append((stage, blob)) # END for each unmerged blob for l in mviter(path_map): l.sort() @@ -568,8 +568,8 @@ def _to_relative_path(self, path): def _preprocess_add_items(self, items): """ Split the items into two lists of path strings and BaseEntries. """ - paths = list() - entries = list() + paths = [] + entries = [] for item in items: if isinstance(item, string_types): @@ -602,7 +602,7 @@ def _store_path(self, filepath, fprogress): @unbare_repo @git_working_dir def _entries_for_paths(self, paths, path_rewriter, fprogress, entries): - entries_added = list() + entries_added = [] if path_rewriter: for path in paths: if osp.isabs(path): @@ -734,7 +734,7 @@ def add(self, items, force=True, fprogress=lambda *args: None, path_rewriter=Non # automatically # paths can be git-added, for everything else we use git-update-index paths, entries = self._preprocess_add_items(items) - entries_added = list() + entries_added = [] # This code needs a working tree, therefore we try not to run it unless required. # That way, we are OK on a bare repository as well. # If there are no paths, the rewriter has nothing to do either @@ -801,7 +801,7 @@ def handle_null_entries(self): def _items_to_rela_paths(self, items): """Returns a list of repo-relative paths from the given items which may be absolute or relative paths, entries or blobs""" - paths = list() + paths = [] for item in items: if isinstance(item, (BaseIndexEntry, (Blob, Submodule))): paths.append(self._to_relative_path(item.path)) @@ -850,7 +850,7 @@ def remove(self, items, working_tree=False, **kwargs): been removed effectively. This is interesting to know in case you have provided a directory or globs. Paths are relative to the repository. """ - args = list() + args = [] if not working_tree: args.append("--cached") args.append("--") @@ -889,7 +889,7 @@ def move(self, items, skip_errors=False, **kwargs): :raise ValueError: If only one item was given GitCommandError: If git could not handle your request""" - args = list() + args = [] if skip_errors: args.append('-k') @@ -902,7 +902,7 @@ def move(self, items, skip_errors=False, **kwargs): # first execute rename in dryrun so the command tells us what it actually does # ( for later output ) - out = list() + out = [] mvlines = self.repo.git.mv(args, paths, **kwargs).splitlines() # parse result - first 0:n/2 lines are 'checking ', the remaining ones @@ -1033,9 +1033,9 @@ def handle_stderr(proc, iter_checked_out_files): # line contents: stderr = stderr.decode(defenc) # git-checkout-index: this already exists - failed_files = list() - failed_reasons = list() - unknown_lines = list() + failed_files = [] + failed_reasons = [] + unknown_lines = [] endings = (' already exists', ' is not in the cache', ' does not exist at stage', ' is unmerged') for line in stderr.splitlines(): if not line.startswith("git checkout-index: ") and not line.startswith("git-checkout-index: "): @@ -1098,7 +1098,7 @@ def handle_stderr(proc, iter_checked_out_files): proc = self.repo.git.checkout_index(args, **kwargs) # FIXME: Reading from GIL! make_exc = lambda: GitCommandError(("git-checkout-index",) + tuple(args), 128, proc.stderr.read()) - checked_out_files = list() + checked_out_files = [] for path in paths: co_path = to_native_path_linux(self._to_relative_path(path)) diff --git a/git/index/fun.py b/git/index/fun.py index c01a32b81..c8912dd23 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -187,7 +187,7 @@ def read_cache(stream): * content_sha is a 20 byte sha on all cache file contents""" version, num_entries = read_header(stream) count = 0 - entries = dict() + entries = {} read = stream.read tell = stream.tell @@ -236,7 +236,7 @@ def write_tree_from_cache(entries, odb, sl, si=0): :param sl: slice indicating the range we should process on the entries list :return: tuple(binsha, list(tree_entry, ...)) a tuple of a sha and a list of tree entries being a tuple of hexsha, mode, name""" - tree_items = list() + tree_items = [] tree_items_append = tree_items.append ci = sl.start end = sl.stop @@ -295,7 +295,7 @@ def aggressive_tree_merge(odb, tree_shas): :param tree_shas: 1, 2 or 3 trees as identified by their binary 20 byte shas If 1 or two, the entries will effectively correspond to the last given tree If 3 are given, a 3 way merge is performed""" - out = list() + out = [] out_append = out.append # one and two way is the same for us, as we don't have to handle an existing diff --git a/git/objects/blob.py b/git/objects/blob.py index 322f6992b..897f892bf 100644 --- a/git/objects/blob.py +++ b/git/objects/blob.py @@ -20,7 +20,7 @@ class Blob(base.IndexObject): file_mode = 0o100644 link_mode = 0o120000 - __slots__ = tuple() + __slots__ = () @property def mime_type(self): diff --git a/git/objects/commit.py b/git/objects/commit.py index f29fbaa28..b7d27d92c 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -316,7 +316,7 @@ def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False, parent_commits = [repo.head.commit] except ValueError: # empty repositories have no head commit - parent_commits = list() + parent_commits = [] # END handle parent commits else: for p in parent_commits: @@ -450,7 +450,7 @@ def _deserialize(self, stream): readline = stream.readline self.tree = Tree(self.repo, hex_to_bin(readline().split()[1]), Tree.tree_id << 12, '') - self.parents = list() + self.parents = [] next_line = None while True: parent_line = readline() diff --git a/git/objects/fun.py b/git/objects/fun.py index d5b3f9026..38dce0a5d 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -50,7 +50,7 @@ def tree_entries_from_data(data): space_ord = ord(' ') len_data = len(data) i = 0 - out = list() + out = [] while i < len_data: mode = 0 @@ -132,18 +132,18 @@ def traverse_trees_recursive(odb, tree_shas, path_prefix): :param path_prefix: a prefix to be added to the returned paths on this level, set it '' for the first iteration :note: The ordering of the returned items will be partially lost""" - trees_data = list() + trees_data = [] nt = len(tree_shas) for tree_sha in tree_shas: if tree_sha is None: - data = list() + data = [] else: data = tree_entries_from_data(odb.stream(tree_sha).read()) # END handle muted trees trees_data.append(data) # END for each sha to get data for - out = list() + out = [] out_append = out.append # find all matching entries and recursively process them together if the match @@ -193,7 +193,7 @@ def traverse_tree_recursive(odb, tree_sha, path_prefix): * [1] mode as int * [2] path relative to the repository :param path_prefix: prefix to prepend to the front of all returned paths""" - entries = list() + entries = [] data = tree_entries_from_data(odb.stream(tree_sha).read()) # unpacking/packing is faster than accessing individual items diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 331512171..f37da34aa 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -3,10 +3,7 @@ import logging import os import stat -try: - from unittest import SkipTest -except ImportError: - from unittest2 import SkipTest +from unittest import SkipTest import uuid import git @@ -63,7 +60,7 @@ class UpdateProgress(RemoteProgress): CLONE, FETCH, UPDWKTREE = [1 << x for x in range(RemoteProgress._num_op_codes, RemoteProgress._num_op_codes + 3)] _num_op_codes = RemoteProgress._num_op_codes + 3 - __slots__ = tuple() + __slots__ = () BEGIN = UpdateProgress.BEGIN @@ -142,7 +139,7 @@ def _get_intermediate_items(self, item): try: return type(self).list_items(item.module()) except InvalidGitRepositoryError: - return list() + return [] # END handle intermediate items @classmethod diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index fbd658d7c..f2035e5b2 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -22,7 +22,7 @@ class RootUpdateProgress(UpdateProgress): 1 << x for x in range(UpdateProgress._num_op_codes, UpdateProgress._num_op_codes + 4)] _num_op_codes = UpdateProgress._num_op_codes + 4 - __slots__ = tuple() + __slots__ = () BEGIN = RootUpdateProgress.BEGIN @@ -38,7 +38,7 @@ class RootModule(Submodule): """A (virtual) Root of all submodules in the given repository. It can be used to more easily traverse all submodules of the master repository""" - __slots__ = tuple() + __slots__ = () k_root_name = '__ROOT__' diff --git a/git/objects/tree.py b/git/objects/tree.py index ed7c24356..d6134e308 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -189,7 +189,7 @@ def __init__(self, repo, binsha, mode=tree_id << 12, path=None): def _get_intermediate_items(cls, index_object): if index_object.type == "tree": return tuple(index_object._iter_convert_to_object(index_object._cache)) - return tuple() + return () def _set_cache_(self, attr): if attr == "_cache": diff --git a/git/objects/util.py b/git/objects/util.py index 5c085aecf..f630f966e 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -153,7 +153,7 @@ def parse_date(string_date): offset = utctz_to_altz(offset) # now figure out the date and time portion - split time - date_formats = list() + date_formats = [] splitter = -1 if ',' in string_date: date_formats.append("%a, %d %b %Y") @@ -248,7 +248,7 @@ class Traversable(object): into one direction. Subclasses only need to implement one function. Instances of the Subclass must be hashable""" - __slots__ = tuple() + __slots__ = () @classmethod def _get_intermediate_items(cls, item): @@ -344,7 +344,7 @@ def addToStack(stack, item, branch_first, depth): class Serializable(object): """Defines methods to serialize and deserialize objects from and into a data stream""" - __slots__ = tuple() + __slots__ = () def _serialize(self, stream): """Serialize the data of this object into the given data stream diff --git a/git/odict.py b/git/odict.py deleted file mode 100644 index f003d14ec..000000000 --- a/git/odict.py +++ /dev/null @@ -1,10 +0,0 @@ -try: - from collections import OrderedDict -except ImportError: - try: - from ordereddict import OrderedDict - except ImportError: - import warnings - warnings.warn("git-python needs the ordereddict module installed in python below 2.6 and below.") - warnings.warn("Using standard dictionary as substitute, and cause reordering when writing git config") - OrderedDict = dict diff --git a/git/refs/head.py b/git/refs/head.py index 9ad890db8..4b0abb062 100644 --- a/git/refs/head.py +++ b/git/refs/head.py @@ -20,7 +20,7 @@ class HEAD(SymbolicReference): HEAD reference.""" _HEAD_NAME = 'HEAD' _ORIG_HEAD_NAME = 'ORIG_HEAD' - __slots__ = tuple() + __slots__ = () def __init__(self, repo, path=_HEAD_NAME): if path != self._HEAD_NAME: diff --git a/git/refs/log.py b/git/refs/log.py index 1c085ef18..fc962680c 100644 --- a/git/refs/log.py +++ b/git/refs/log.py @@ -32,7 +32,7 @@ class RefLogEntry(tuple): """Named tuple allowing easy access to the revlog data fields""" _re_hexsha_only = re.compile('^[0-9A-Fa-f]{40}$') - __slots__ = tuple() + __slots__ = () def __repr__(self): """Representation of ourselves in git reflog format""" @@ -48,13 +48,13 @@ def format(self): """:return: a string suitable to be placed in a reflog file""" act = self.actor time = self.time - return u"{0} {1} {2} <{3}> {4!s} {5}\t{6}\n".format(self.oldhexsha, - self.newhexsha, - act.name, - act.email, - time[0], - altz_to_utctz_str(time[1]), - self.message) + return u"{} {} {} <{}> {!s} {}\t{}\n".format(self.oldhexsha, + self.newhexsha, + act.name, + act.email, + time[0], + altz_to_utctz_str(time[1]), + self.message) @property def oldhexsha(self): diff --git a/git/refs/reference.py b/git/refs/reference.py index 734ed8b9e..aaa9b63fe 100644 --- a/git/refs/reference.py +++ b/git/refs/reference.py @@ -27,7 +27,7 @@ class Reference(SymbolicReference, LazyMixin, Iterable): """Represents a named reference to any object. Subclasses may apply restrictions though, i.e. Heads can only point to commits.""" - __slots__ = tuple() + __slots__ = () _points_to_commits_only = False _resolve_ref_on_create = True _common_path_default = "refs" diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 8efeafc5c..a8ca6538f 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -444,7 +444,7 @@ def delete(cls, repo, path): pack_file_path = cls._get_packed_refs_path(repo) try: with open(pack_file_path, 'rb') as reader: - new_lines = list() + new_lines = [] made_change = False dropped_last_line = False for line in reader: diff --git a/git/refs/tag.py b/git/refs/tag.py index 37ee1240d..8f88c5225 100644 --- a/git/refs/tag.py +++ b/git/refs/tag.py @@ -17,7 +17,7 @@ class TagReference(Reference): if tagref.tag is not None: print(tagref.tag.message)""" - __slots__ = tuple() + __slots__ = () _common_path_default = "refs/tags" @property diff --git a/git/remote.py b/git/remote.py index 813566a23..8aec68e15 100644 --- a/git/remote.py +++ b/git/remote.py @@ -542,7 +542,7 @@ def urls(self): if ' Push URL:' in line: yield line.split(': ')[-1] except GitCommandError as ex: - if any([msg in str(ex) for msg in ['correct access rights', 'cannot run ssh']]): + if any(msg in str(ex) for msg in ['correct access rights', 'cannot run ssh']): # If ssh is not setup to access this repository, see issue 694 result = Git().execute( ['git', 'config', '--get', 'remote.%s.url' % self.name] @@ -663,7 +663,7 @@ def _get_fetch_info_from_stderr(self, proc, progress): # lines which are no progress are fetch info lines # this also waits for the command to finish # Skip some progress lines that don't provide relevant information - fetch_info_lines = list() + fetch_info_lines = [] # Basically we want all fetch info lines which appear to be in regular form, and thus have a # command character. Everything else we ignore, cmds = set(FetchInfo._flag_map.keys()) diff --git a/git/repo/base.py b/git/repo/base.py index ba589e11b..26c7c7e5f 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -519,7 +519,7 @@ def merge_base(self, *rev, **kwargs): raise ValueError("Please specify at least two revs, got only %i" % len(rev)) # end handle input - res = list() + res = [] try: lines = self.git.merge_base(*rev, **kwargs).splitlines() except GitCommandError as err: @@ -580,7 +580,7 @@ def _get_alternates(self): alts = f.read().decode(defenc) return alts.strip().splitlines() else: - return list() + return [] def _set_alternates(self, alts): """Sets the alternates @@ -664,7 +664,7 @@ def _get_untracked_files(self, *args, **kwargs): **kwargs) # Untracked files preffix in porcelain mode prefix = "?? " - untracked_files = list() + untracked_files = [] for line in proc.stdout: line = line.decode(defenc) if not line.startswith(prefix): @@ -704,7 +704,7 @@ def blame_incremental(self, rev, file, **kwargs): should get a continuous range spanning all line numbers in the file. """ data = self.git.blame(rev, '--', file, p=True, incremental=True, stdout_as_string=False, **kwargs) - commits = dict() + commits = {} stream = (line for line in data.split(b'\n') if line) while True: @@ -716,7 +716,7 @@ def blame_incremental(self, rev, file, **kwargs): if hexsha not in commits: # Now read the next few lines and build up a dict of properties # for this commit - props = dict() + props = {} while True: line = next(stream) if line == b'boundary': @@ -767,8 +767,8 @@ def blame(self, rev, file, incremental=False, **kwargs): return self.blame_incremental(rev, file, **kwargs) data = self.git.blame(rev, '--', file, p=True, stdout_as_string=False, **kwargs) - commits = dict() - blames = list() + commits = {} + blames = [] info = None keepends = True @@ -1001,7 +1001,7 @@ def archive(self, ostream, treeish=None, prefix=None, **kwargs): if prefix and 'prefix' not in kwargs: kwargs['prefix'] = prefix kwargs['output_stream'] = ostream - path = kwargs.pop('path', list()) + path = kwargs.pop('path', []) if not isinstance(path, (tuple, list)): path = [path] # end assure paths is list diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index cb46173dc..1c06010f4 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -15,6 +15,7 @@ import tempfile import textwrap import time +import unittest from git.compat import string_types, is_win from git.util import rmtree, cwd @@ -23,11 +24,6 @@ import os.path as osp -if sys.version_info[0:2] == (2, 6): - import unittest2 as unittest -else: - import unittest - TestCase = unittest.TestCase SkipTest = unittest.SkipTest skipIf = unittest.skipIf @@ -348,7 +344,6 @@ class TestBase(TestCase): of the project history ( to assure tests don't fail for others ). """ - # On py26, unittest2 has assertRaisesRegex # On py3, unittest has assertRaisesRegex # On py27, we use unittest, which names it differently: if sys.version_info[0:2] == (2, 7): diff --git a/git/test/performance/test_odb.py b/git/test/performance/test_odb.py index 425af84a5..8bd614f28 100644 --- a/git/test/performance/test_odb.py +++ b/git/test/performance/test_odb.py @@ -28,11 +28,11 @@ def test_random_access(self): # GET TREES # walk all trees of all commits st = time() - blobs_per_commit = list() + blobs_per_commit = [] nt = 0 for commit in commits: tree = commit.tree - blobs = list() + blobs = [] for item in tree.traverse(): nt += 1 if item.type == 'blob': diff --git a/git/test/performance/test_streams.py b/git/test/performance/test_streams.py index 3909d8ff1..2e3772a02 100644 --- a/git/test/performance/test_streams.py +++ b/git/test/performance/test_streams.py @@ -69,7 +69,7 @@ def test_large_data_streaming(self, rwrepo): # reading in chunks of 1 MiB cs = 512 * 1000 - chunks = list() + chunks = [] st = time() ostream = ldb.stream(binsha) while True: diff --git a/git/test/test_base.py b/git/test/test_base.py index 69f161bee..2132806be 100644 --- a/git/test/test_base.py +++ b/git/test/test_base.py @@ -7,10 +7,7 @@ import os import sys import tempfile -try: - from unittest import SkipTest, skipIf -except ImportError: - from unittest2 import SkipTest, skipIf +from unittest import SkipTest, skipIf from git import ( Blob, diff --git a/git/test/test_diff.py b/git/test/test_diff.py index 48a5a641f..d21dde624 100644 --- a/git/test/test_diff.py +++ b/git/test/test_diff.py @@ -217,7 +217,7 @@ def test_diff_with_spaces(self): def test_diff_interface(self): # test a few variations of the main diff routine - assertion_map = dict() + assertion_map = {} for i, commit in enumerate(self.rorepo.iter_commits('0.1.6', max_count=2)): diff_item = commit if i % 2 == 0: diff --git a/git/test/test_docs.py b/git/test/test_docs.py index 1ba3f4821..67ffb9341 100644 --- a/git/test/test_docs.py +++ b/git/test/test_docs.py @@ -173,7 +173,7 @@ def update(self, op_code, cur_count, max_count=None, message=''): # [14-test_init_repo_object] # create a new submodule and check it out on the spot, setup to track master branch of `bare_repo` - # As our GitPython repository has submodules already that point to github, make sure we don't + # As our GitPython repository has submodules already that point to GitHub, make sure we don't # interact with them for sm in cloned_repo.submodules: assert not sm.remove().exists() # after removal, the sm doesn't exist anymore diff --git a/git/test/test_fun.py b/git/test/test_fun.py index 5e32a1f9d..314fb734a 100644 --- a/git/test/test_fun.py +++ b/git/test/test_fun.py @@ -2,11 +2,7 @@ from stat import S_IFDIR, S_IFREG, S_IFLNK from os import stat import os.path as osp - -try: - from unittest import skipIf, SkipTest -except ImportError: - from unittest2 import skipIf, SkipTest +from unittest import skipIf, SkipTest from git import Git from git.compat import PY3 @@ -215,7 +211,7 @@ def assert_entries(entries, num_entries, has_conflict=False): def _assert_tree_entries(self, entries, num_trees): for entry in entries: assert len(entry) == num_trees - paths = set(e[2] for e in entry if e) + paths = {e[2] for e in entry if e} # only one path per set of entries assert len(paths) == 1 diff --git a/git/test/test_git.py b/git/test/test_git.py index 059f90c0e..c6180f7c9 100644 --- a/git/test/test_git.py +++ b/git/test/test_git.py @@ -91,7 +91,7 @@ def test_it_transforms_kwargs_into_git_command_arguments(self): # order is undefined res = self.git.transform_kwargs(**{'s': True, 't': True}) - self.assertEqual(set(['-s', '-t']), set(res)) + self.assertEqual({'-s', '-t'}, set(res)) def test_it_executes_git_to_shell_and_returns_result(self): assert_match(r'^git version [\d\.]{2}.*$', self.git.execute(["git", "version"])) diff --git a/git/test/test_index.py b/git/test/test_index.py index 757bec9f9..9be4031d1 100644 --- a/git/test/test_index.py +++ b/git/test/test_index.py @@ -11,12 +11,8 @@ S_ISLNK, ST_MODE ) -import sys import tempfile -try: - from unittest import skipIf -except ImportError: - from unittest2 import skipIf +from unittest import skipIf from git import ( IndexFile, @@ -101,7 +97,7 @@ def _fprogress_add(self, path, done, item): def _reset_progress(self): # maps paths to the count of calls - self._fprogress_map = dict() + self._fprogress_map = {} def _assert_entries(self, entries): for entry in entries: @@ -131,7 +127,7 @@ def test_index_file_base(self): # test stage index_merge = IndexFile(self.rorepo, fixture_path("index_merge")) self.assertEqual(len(index_merge.entries), 106) - assert len(list(e for e in index_merge.entries.values() if e.stage != 0)) + assert len([e for e in index_merge.entries.values() if e.stage != 0]) # write the data - it must match the original tmpfile = tempfile.mktemp() @@ -145,14 +141,14 @@ def _cmp_tree_index(self, tree, index): if isinstance(tree, str): tree = self.rorepo.commit(tree).tree - blist = list() + blist = [] for blob in tree.traverse(predicate=lambda e, d: e.type == "blob", branch_first=False): assert (blob.path, 0) in index.entries blist.append(blob) # END for each blob in tree if len(blist) != len(index.entries): - iset = set(k[0] for k in index.entries.keys()) - bset = set(b.path for b in blist) + iset = {k[0] for k in index.entries.keys()} + bset = {b.path for b in blist} raise AssertionError("CMP Failed: Missing entries in index: %s, missing in tree: %s" % (bset - iset, iset - bset)) # END assertion message @@ -168,9 +164,7 @@ def add_bad_blob(): except Exception as ex: msg_py3 = "required argument is not an integer" msg_py2 = "cannot convert argument to integer" - msg_py26 = "unsupported operand type(s) for &: 'str' and 'long'" - assert msg_py2 in str(ex) or msg_py3 in str(ex) or \ - msg_py26 in str(ex), str(ex) + assert msg_py2 in str(ex) or msg_py3 in str(ex) ## 2nd time should not fail due to stray lock file try: @@ -180,9 +174,6 @@ def add_bad_blob(): @with_rw_repo('0.1.6') def test_index_file_from_tree(self, rw_repo): - if sys.version_info < (2, 7): - ## Skipped, not `assertRaisesRegexp` in py2.6 - return common_ancestor_sha = "5117c9c8a4d3af19a9958677e45cda9269de1541" cur_sha = "4b43ca7ff72d5f535134241e7c797ddc9c7a3573" other_sha = "39f85c4358b7346fee22169da9cad93901ea9eb9" @@ -199,7 +190,7 @@ def test_index_file_from_tree(self, rw_repo): # merge three trees - here we have a merge conflict three_way_index = IndexFile.from_tree(rw_repo, common_ancestor_sha, cur_sha, other_sha) - assert len(list(e for e in three_way_index.entries.values() if e.stage != 0)) + assert len([e for e in three_way_index.entries.values() if e.stage != 0]) # ITERATE BLOBS merge_required = lambda t: t[0] != 0 @@ -235,7 +226,7 @@ def test_index_file_from_tree(self, rw_repo): def test_index_merge_tree(self, rw_repo): # A bit out of place, but we need a different repo for this: self.assertNotEqual(self.rorepo, rw_repo) - self.assertEqual(len(set((self.rorepo, self.rorepo, rw_repo, rw_repo))), 2) + self.assertEqual(len({self.rorepo, self.rorepo, rw_repo, rw_repo}), 2) # SINGLE TREE MERGE # current index is at the (virtual) cur_commit @@ -536,7 +527,7 @@ def mixed_iterator(): # same index, no parents commit_message = "index without parents" - commit_no_parents = index.commit(commit_message, parent_commits=list(), head=True) + commit_no_parents = index.commit(commit_message, parent_commits=[], head=True) self.assertEqual(commit_no_parents.message, commit_message) self.assertEqual(len(commit_no_parents.parents), 0) self.assertEqual(cur_head.commit, commit_no_parents) @@ -891,10 +882,10 @@ def test_commit_msg_hook_success(self, rw_repo): _make_hook( index.repo.git_dir, 'commit-msg', - 'echo -n " {0}" >> "$1"'.format(from_hook_message) + 'echo -n " {}" >> "$1"'.format(from_hook_message) ) new_commit = index.commit(commit_message) - self.assertEqual(new_commit.message, u"{0} {1}".format(commit_message, from_hook_message)) + self.assertEqual(new_commit.message, u"{} {}".format(commit_message, from_hook_message)) @with_rw_repo('HEAD', bare=True) def test_commit_msg_hook_fail(self, rw_repo): diff --git a/git/test/test_refs.py b/git/test/test_refs.py index f885617e8..348c3d482 100644 --- a/git/test/test_refs.py +++ b/git/test/test_refs.py @@ -45,7 +45,7 @@ def test_from_path(self): TagReference(self.rorepo, "refs/invalid/tag", check_path=False) def test_tag_base(self): - tag_object_refs = list() + tag_object_refs = [] for tag in self.rorepo.tags: assert "refs/tags" in tag.path assert tag.name diff --git a/git/test/test_remote.py b/git/test/test_remote.py index ad9210f64..7c1711c27 100644 --- a/git/test/test_remote.py +++ b/git/test/test_remote.py @@ -6,10 +6,7 @@ import random import tempfile -try: - from unittest import skipIf -except ImportError: - from unittest2 import skipIf +from unittest import skipIf from git import ( RemoteProgress, @@ -47,8 +44,8 @@ class TestRemoteProgress(RemoteProgress): def __init__(self): super(TestRemoteProgress, self).__init__() - self._seen_lines = list() - self._stages_per_op = dict() + self._seen_lines = [] + self._stages_per_op = {} self._num_progress_messages = 0 def _parse_progress_line(self, line): diff --git a/git/test/test_repo.py b/git/test/test_repo.py index 2c3ad9570..97eac4aeb 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -9,12 +9,8 @@ import itertools import os import pickle -import sys import tempfile -try: - from unittest import skipIf, SkipTest -except ImportError: - from unittest2 import skipIf, SkipTest +from unittest import skipIf, SkipTest try: import pathlib @@ -364,9 +360,6 @@ def test_archive(self): @patch.object(Git, '_call_process') def test_should_display_blame_information(self, git): - if sys.version_info < (2, 7): - ## Skipped, not `assertRaisesRegexp` in py2.6 - return git.return_value = fixture('blame') b = self.rorepo.blame('master', 'lib/git.py') assert_equal(13, len(b)) @@ -522,7 +515,7 @@ def test_comparison_and_hash(self): # this is only a preliminary test, more testing done in test_index self.assertEqual(self.rorepo, self.rorepo) self.assertFalse(self.rorepo != self.rorepo) - self.assertEqual(len(set((self.rorepo, self.rorepo))), 1) + self.assertEqual(len({self.rorepo, self.rorepo}), 1) @with_rw_directory def test_tilde_and_env_vars_in_repo_path(self, rw_dir): @@ -792,8 +785,6 @@ def test_rev_parse(self): def test_repo_odbtype(self): target_type = GitCmdObjectDB - if sys.version_info[:2] < (2, 5): - target_type = GitCmdObjectDB self.assertIsInstance(self.rorepo.odb, target_type) def test_submodules(self): diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py index 5c8a2798a..0bf763801 100644 --- a/git/test/test_submodule.py +++ b/git/test/test_submodule.py @@ -3,10 +3,7 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php import os import sys -try: - from unittest import skipIf -except ImportError: - from unittest2 import skipIf +from unittest import skipIf import git from git.cmd import Git @@ -267,7 +264,7 @@ def _do_base_tests(self, rwrepo): self.failUnlessRaises(ValueError, csm.remove, module=False, configuration=False) # module() is supposed to point to gitdb, which has a child-submodule whose URL is still pointing - # to github. To save time, we will change it to + # to GitHub. To save time, we will change it to csm.set_parent_commit(csm.repo.head.commit) with csm.config_writer() as cw: cw.set_value('url', self._small_repo_url()) diff --git a/git/test/test_tree.py b/git/test/test_tree.py index 5fd4d760b..dc23f29ca 100644 --- a/git/test/test_tree.py +++ b/git/test/test_tree.py @@ -6,10 +6,7 @@ from io import BytesIO import sys -try: - from unittest import skipIf -except ImportError: - from unittest2 import skipIf +from unittest import skipIf from git import ( Tree, @@ -64,7 +61,7 @@ def test_serializable(self): def test_traverse(self): root = self.rorepo.tree('0.1.6') num_recursive = 0 - all_items = list() + all_items = [] for obj in root.traverse(): if "/" in obj.path: num_recursive += 1 @@ -82,7 +79,7 @@ def test_traverse(self): # only choose trees trees_only = lambda i, d: i.type == "tree" trees = list(root.traverse(predicate=trees_only)) - assert len(trees) == len(list(i for i in root.traverse() if trees_only(i, 0))) + assert len(trees) == len([i for i in root.traverse() if trees_only(i, 0)]) # test prune lib_folder = lambda t, d: t.path == "lib" @@ -91,7 +88,7 @@ def test_traverse(self): # trees and blobs assert len(set(trees) | set(root.trees)) == len(trees) - assert len(set(b for b in root if isinstance(b, Blob)) | set(root.blobs)) == len(root.blobs) + assert len({b for b in root if isinstance(b, Blob)} | set(root.blobs)) == len(root.blobs) subitem = trees[0][0] assert "/" in subitem.path assert subitem.name == osp.basename(subitem.path) diff --git a/git/test/test_util.py b/git/test/test_util.py index d30c8376d..b7925c84f 100644 --- a/git/test/test_util.py +++ b/git/test/test_util.py @@ -6,10 +6,7 @@ import tempfile import time -try: - from unittest import skipIf -except ImportError: - from unittest2 import skipIf +from unittest import skipIf import ddt diff --git a/git/util.py b/git/util.py index 52029fede..1186d3110 100644 --- a/git/util.py +++ b/git/util.py @@ -14,10 +14,7 @@ import shutil import stat import time -try: - from unittest import SkipTest -except ImportError: - from unittest2 import SkipTest +from unittest import SkipTest from gitdb.util import (# NOQA @IgnorePep8 make_sha, @@ -372,7 +369,7 @@ class RemoteProgress(object): re_op_relative = re.compile(r"(remote: )?([\w\s]+):\s+(\d+)% \((\d+)/(\d+)\)(.*)") def __init__(self): - self._seen_ops = list() + self._seen_ops = [] self._cur_line = None self.error_lines = [] self.other_lines = [] @@ -395,7 +392,7 @@ def _parse_progress_line(self, line): return [] sub_lines = line.split('\r') - failed_lines = list() + failed_lines = [] for sline in sub_lines: # find escape characters and cut them away - regex will not work with # them as they are non-ascii. As git might expect a tty, it will send them @@ -673,7 +670,7 @@ def _list_from_string(cls, repo, text): """Create a Stat object from output retrieved by git-diff. :return: git.Stat""" - hsh = {'total': {'insertions': 0, 'deletions': 0, 'lines': 0, 'files': 0}, 'files': dict()} + hsh = {'total': {'insertions': 0, 'deletions': 0, 'lines': 0, 'files': 0}, 'files': {}} for line in text.splitlines(): (raw_insertions, raw_deletions, filename) = line.split("\t") insertions = raw_insertions != '-' and int(raw_insertions) or 0 @@ -920,7 +917,7 @@ class Iterable(object): """Defines an interface for iterable items which is to assure a uniform way to retrieve and iterate items within the git repository""" - __slots__ = tuple() + __slots__ = () _id_attribute_ = "attribute that most suitably identifies your instance" @classmethod diff --git a/requirements.txt b/requirements.txt index a8e7a7a8a..396446062 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ gitdb>=0.6.4 ddt>=1.1.1 -unittest2; python_version < '2.7' diff --git a/setup.py b/setup.py index 47523e033..2703a9cac 100755 --- a/setup.py +++ b/setup.py @@ -9,8 +9,6 @@ from distutils.command.build_py import build_py as _build_py from setuptools.command.sdist import sdist as _sdist -import pkg_resources -import logging import os import sys from os import path @@ -47,7 +45,7 @@ def make_release_tree(self, base_dir, files): def _stamp_version(filename): - found, out = False, list() + found, out = False, [] try: with open(filename, 'r') as f: for line in f: @@ -66,26 +64,7 @@ def _stamp_version(filename): install_requires = ['gitdb2 >= 2.0.0'] -extras_require = { - ':python_version == "2.6"': ['ordereddict'], -} test_requires = ['ddt>=1.1.1'] -if sys.version_info[:2] < (2, 7): - test_requires.append('mock') - -try: - if 'bdist_wheel' not in sys.argv: - for key, value in extras_require.items(): - if key.startswith(':') and pkg_resources.evaluate_marker(key[1:]): - install_requires.extend(value) -except Exception: - logging.getLogger(__name__).exception( - 'Something went wrong calculating platform specific dependencies, so ' - "you're getting them all!" - ) - for key, value in extras_require.items(): - if key.startswith(':'): - install_requires.extend(value) # end setup( @@ -101,7 +80,7 @@ def _stamp_version(filename): package_data={'git.test': ['fixtures/*']}, package_dir={'git': 'git'}, license="BSD License", - python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*', + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', requires=['gitdb2 (>=2.0.0)'], install_requires=install_requires, test_requirements=test_requires + install_requires, @@ -126,10 +105,8 @@ def _stamp_version(filename): "Operating System :: MacOS :: MacOS X", "Programming Language :: Python", "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", diff --git a/test-requirements.txt b/test-requirements.txt index 8f13395d5..1cea3aa21 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,4 +3,4 @@ coverage flake8 nose -mock; python_version<='2.7' +mock; python_version=='2.7' \ No newline at end of file diff --git a/tox.ini b/tox.ini index 9f03872b2..ed09c08bf 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27,py33,py34,py35,flake8 +envlist = py27,py34,py35,py36,flake8 [testenv] commands = nosetests {posargs}