Skip to content

Commit cc5e6d8

Browse files
committed
Improved file and path handling
- Replaced all open() statements with io.open(), this improves unicode file handling - Removed a bunch of hard-coded forward slashes to use os.path.join instead, this is more OS agnostic - Replaced os.path.abspath with os.path.realpath for improved symlink handling This commit relates to jorisroovers#20
1 parent 3fc4c03 commit cc5e6d8

13 files changed

+87
-76
lines changed

.pylintrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ variable-rgx=[a-z_][a-z0-9_]{0,30}$
2424
argument-rgx=[a-z_][a-z0-9_]{1,30}$
2525

2626
# Method names should be at least 3 characters long
27-
# and be lowecased with underscores
27+
# and be lower-cased with underscores
2828
method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$
2929

3030
# Allow 'id' as variable name everywhere

gitlint/cli.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def uninstall_hook(ctx):
285285
def generate_config(ctx):
286286
""" Generates a sample gitlint config file. """
287287
path = click.prompt('Please specify a location for the sample gitlint config file', default=DEFAULT_CONFIG_FILE)
288-
path = os.path.abspath(path)
288+
path = os.path.realpath(path)
289289
dir_name = os.path.dirname(path)
290290
if not os.path.exists(dir_name):
291291
click.echo(u"Error: Directory '{0}' does not exist.".format(dir_name), err=True)

gitlint/config.py

+10-12
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,13 @@
66
from configparser import ConfigParser, Error as ConfigParserError # pragma: no cover, pylint: disable=import-error
77

88
import copy
9+
import io
910
import re
1011
import os
1112
import shutil
1213

13-
try:
14-
# python >= 2.7
15-
from collections import OrderedDict # pylint: disable=no-name-in-module
16-
except ImportError: # pragma: no cover
17-
# python 2.4-2.6
18-
from ordereddict import OrderedDict # pragma: no cover
19-
20-
from gitlint.utils import ustr
14+
from collections import OrderedDict
15+
from gitlint.utils import ustr, DEFAULT_ENCODING
2116
from gitlint import rules # For some weird reason pylint complains about this, pylint: disable=unused-import
2217
from gitlint import options
2318
from gitlint import rule_finder
@@ -75,7 +70,7 @@ def __init__(self):
7570
self._debug = options.BoolOption('debug', False, "Enable debug mode")
7671
self._extra_path = None
7772
target_description = "Path of the target git repository (default=current working directory)"
78-
self._target = options.PathOption('target', os.path.abspath(os.getcwd()), target_description)
73+
self._target = options.PathOption('target', os.path.realpath(os.getcwd()), target_description)
7974
self._ignore = options.ListOption('ignore', [], 'List of rule-ids to ignore')
8075
self._contrib = options.ListOption('contrib', [], 'List of contrib-rules to enable')
8176
self._config_path = None
@@ -349,10 +344,13 @@ def set_from_config_file(self, filename):
349344
""" Loads lint config from a ini-style config file """
350345
if not os.path.exists(filename):
351346
raise LintConfigError(u"Invalid file path: {0}".format(filename))
352-
self._config_path = os.path.abspath(filename)
347+
self._config_path = os.path.realpath(filename)
353348
try:
354349
parser = ConfigParser()
355-
parser.read(filename)
350+
351+
with io.open(filename, encoding=DEFAULT_ENCODING) as config_file:
352+
# readfp() is deprecated in python 3.2+, but compatible with 2.7
353+
parser.readfp(config_file, filename) # pylint: disable=deprecated-method
356354

357355
for section_name in parser.sections():
358356
for option_name, option_value in parser.items(section_name):
@@ -394,7 +392,7 @@ def clone(self):
394392
return builder
395393

396394

397-
GITLINT_CONFIG_TEMPLATE_SRC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "files/gitlint")
395+
GITLINT_CONFIG_TEMPLATE_SRC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "files/gitlint")
398396

399397

400398
class LintConfigGenerator(object):

gitlint/hooks.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import io
12
import shutil
23
import os
34
import stat
45

5-
COMMIT_MSG_HOOK_SRC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "files/commit-msg")
6-
COMMIT_MSG_HOOK_DST_PATH = ".git/hooks/commit-msg"
7-
HOOKS_DIR_PATH = ".git/hooks"
6+
from gitlint.utils import DEFAULT_ENCODING
7+
8+
COMMIT_MSG_HOOK_SRC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "files/commit-msg")
9+
COMMIT_MSG_HOOK_DST_PATH = os.path.join(".git", "hooks", "commit-msg")
10+
HOOKS_DIR_PATH = os.path.join(".git", "hooks")
811
GITLINT_HOOK_IDENTIFIER = "### gitlint commit-msg hook start ###\n"
912

1013

@@ -17,12 +20,12 @@ class GitHookInstaller(object):
1720

1821
@staticmethod
1922
def commit_msg_hook_path(lint_config):
20-
return os.path.abspath(os.path.join(lint_config.target, COMMIT_MSG_HOOK_DST_PATH))
23+
return os.path.realpath(os.path.join(lint_config.target, COMMIT_MSG_HOOK_DST_PATH))
2124

2225
@staticmethod
2326
def _assert_git_repo(target):
2427
""" Asserts that a given target directory is a git repository """
25-
hooks_dir = os.path.abspath(os.path.join(target, HOOKS_DIR_PATH))
28+
hooks_dir = os.path.realpath(os.path.join(target, HOOKS_DIR_PATH))
2629
if not os.path.isdir(hooks_dir):
2730
raise GitHookInstallerError(u"{0} is not a git repository.".format(target))
2831

@@ -48,8 +51,8 @@ def uninstall_commit_msg_hook(lint_config):
4851
if not os.path.exists(dest_path):
4952
raise GitHookInstallerError(u"There is no commit-msg hook present in {0}.".format(dest_path))
5053

51-
with open(dest_path) as file:
52-
lines = file.readlines()
54+
with io.open(dest_path, encoding=DEFAULT_ENCODING) as fp:
55+
lines = fp.readlines()
5356
if len(lines) < 2 or lines[1] != GITLINT_HOOK_IDENTIFIER:
5457
msg = u"The commit-msg hook in {0} was not installed by gitlint (or it was modified).\n" + \
5558
u"Uninstallation of 3th party or modified gitlint hooks is not supported."

gitlint/options.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,4 @@ def set(self, value):
116116
if error_msg:
117117
raise RuleOptionError(error_msg)
118118

119-
self.value = os.path.abspath(value)
119+
self.value = os.path.realpath(value)

gitlint/tests/base.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import io
12
import logging
23
import os
34

@@ -9,7 +10,8 @@
910
import unittest
1011

1112
from gitlint.git import GitContext
12-
from gitlint.utils import ustr, LOG_FORMAT
13+
from gitlint.utils import ustr, LOG_FORMAT, DEFAULT_ENCODING
14+
1315

1416
# unittest2's assertRaisesRegex doesn't do unicode comparison.
1517
# Let's monkeypatch the str() function to point to unicode() so that it does :)
@@ -54,7 +56,7 @@ def get_sample_path(filename=""):
5456
def get_sample(filename=""):
5557
""" Read and return the contents of a file in gitlint/tests/samples """
5658
sample_path = BaseTestCase.get_sample_path(filename)
57-
with open(sample_path) as content:
59+
with io.open(sample_path, encoding=DEFAULT_ENCODING) as content:
5860
sample = ustr(content.read())
5961
return sample
6062

@@ -63,7 +65,7 @@ def get_expected(filename="", variable_dict=None):
6365
""" Utility method to read an expected file from gitlint/tests/expected and return it as a string.
6466
Optionally replace template variables specified by variable_dict. """
6567
expected_path = os.path.join(BaseTestCase.EXPECTED_DIR, filename)
66-
with open(expected_path) as content:
68+
with io.open(expected_path, encoding=DEFAULT_ENCODING) as content:
6769
expected = ustr(content.read())
6870

6971
if variable_dict:

gitlint/tests/test_cli.py

+19-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33
import contextlib
4+
import io
45
import os
56
import sys
67
import platform
@@ -12,7 +13,7 @@
1213
from StringIO import StringIO
1314
except ImportError:
1415
# python 3.x
15-
from io import StringIO
16+
from io import StringIO # pylint: disable=ungrouped-imports
1617

1718
from click.testing import CliRunner
1819

@@ -30,6 +31,7 @@
3031
from gitlint import hooks
3132
from gitlint import __version__
3233
from gitlint import config
34+
from gitlint.utils import DEFAULT_ENCODING
3335

3436

3537
@contextlib.contextmanager
@@ -194,8 +196,8 @@ def test_msg_filename(self, _):
194196

195197
with tempdir() as tmpdir:
196198
msg_filename = os.path.join(tmpdir, "msg")
197-
with open(msg_filename, 'w') as f:
198-
f.write("Commït title\n")
199+
with io.open(msg_filename, 'w', encoding=DEFAULT_ENCODING) as f:
200+
f.write(u"Commït title\n")
199201

200202
with patch('gitlint.display.stderr', new=StringIO()) as stderr:
201203
result = self.cli.invoke(cli.cli, ["--msg-filename", msg_filename])
@@ -260,7 +262,7 @@ def test_debug(self, sh, _):
260262
u"file6.txt\npåth/to/file7.txt\n"]
261263

262264
with patch('gitlint.display.stderr', new=StringIO()) as stderr:
263-
config_path = self.get_sample_path("config/gitlintconfig")
265+
config_path = self.get_sample_path(os.path.join("config", "gitlintconfig"))
264266
result = self.cli.invoke(cli.cli, ["--config", config_path, "--debug", "--commits",
265267
"foo...bar"])
266268

@@ -276,8 +278,8 @@ def test_debug(self, sh, _):
276278
u"DEBUG: gitlint.cli Python version: {0}".format(sys.version),
277279
u"DEBUG: gitlint.cli Git version: git version 1.2.3",
278280
u"DEBUG: gitlint.cli Gitlint version: {0}".format(__version__),
279-
self.get_expected('debug_configuration_output1', {'config_path': config_path,
280-
'target': os.path.abspath(os.getcwd())}),
281+
self.get_expected('debug_configuration_output1',
282+
{'config_path': config_path, 'target': os.path.realpath(os.getcwd())}),
281283
u"DEBUG: gitlint.cli No --msg-filename flag, no or empty data passed to stdin. " +
282284
u"Attempting to read from the local repo.",
283285
u"DEBUG: gitlint.lint Linting commit 6f29bf81a8322a04071bb794666e48c443a90360",
@@ -307,7 +309,7 @@ def test_extra_path(self, _):
307309

308310
# Test extra-path pointing to a file
309311
with patch('gitlint.display.stderr', new=StringIO()) as stderr:
310-
extra_path = self.get_sample_path("user_rules/my_commit_rules.py")
312+
extra_path = self.get_sample_path(os.path.join("user_rules", "my_commit_rules.py"))
311313
result = self.cli.invoke(cli.cli, ["--extra-path", extra_path, "--debug"])
312314
expected_output = u"1: UC1 Commit violåtion 1: \"Contënt 1\"\n" + \
313315
"3: B6 Body message is missing\n"
@@ -333,7 +335,7 @@ def test_contrib_negative(self, _):
333335
def test_config_file(self, _):
334336
""" Test for --config option """
335337
with patch('gitlint.display.stderr', new=StringIO()) as stderr:
336-
config_path = self.get_sample_path("config/gitlintconfig")
338+
config_path = self.get_sample_path(os.path.join("config", "gitlintconfig"))
337339
result = self.cli.invoke(cli.cli, ["--config", config_path])
338340
self.assertEqual(result.output, "")
339341
self.assertEqual(stderr.getvalue(), "1: T5\n3: B6\n")
@@ -358,7 +360,7 @@ def test_config_file_negative(self):
358360
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
359361

360362
# Invalid config file
361-
config_path = self.get_sample_path("config/invalid-option-value")
363+
config_path = self.get_sample_path(os.path.join("config", "invalid-option-value"))
362364
result = self.cli.invoke(cli.cli, ["--config", config_path])
363365
self.assertEqual(result.exit_code, self.CONFIG_ERROR_CODE)
364366

@@ -382,7 +384,7 @@ def test_target_negative(self):
382384
self.assertEqual(result.output.split("\n")[3], expected_msg)
383385

384386
# try setting a file as target
385-
target_path = self.get_sample_path("config/gitlintconfig")
387+
target_path = self.get_sample_path(os.path.join("config", "gitlintconfig"))
386388
result = self.cli.invoke(cli.cli, ["--target", target_path])
387389
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
388390
expected_msg = u"Error: Invalid value for \"--target\": Directory \"{0}\" is a file.".format(target_path)
@@ -394,9 +396,9 @@ def test_generate_config(self, generate_config):
394396
result = self.cli.invoke(cli.cli, ["generate-config"], input=u"tëstfile\n")
395397
self.assertEqual(result.exit_code, 0)
396398
expected_msg = u"Please specify a location for the sample gitlint config file [.gitlint]: tëstfile\n" + \
397-
u"Successfully generated {0}\n".format(os.path.abspath(u"tëstfile"))
399+
u"Successfully generated {0}\n".format(os.path.realpath(u"tëstfile"))
398400
self.assertEqual(result.output, expected_msg)
399-
generate_config.assert_called_once_with(os.path.abspath(u"tëstfile"))
401+
generate_config.assert_called_once_with(os.path.realpath(u"tëstfile"))
400402

401403
def test_generate_config_negative(self):
402404
""" Negative test for the generate-config subcommand """
@@ -408,7 +410,7 @@ def test_generate_config_negative(self):
408410
self.assertEqual(result.output, expected_msg)
409411

410412
# Existing file
411-
sample_path = self.get_sample_path("config/gitlintconfig")
413+
sample_path = self.get_sample_path(os.path.join("config", "gitlintconfig"))
412414
result = self.cli.invoke(cli.cli, ["generate-config"], input=sample_path)
413415
self.assertEqual(result.exit_code, self.USAGE_ERROR_CODE)
414416
expected_msg = "Please specify a location for the sample gitlint " + \
@@ -443,7 +445,7 @@ def test_install_hook(self, install_hook):
443445
self.assertEqual(result.exit_code, 0)
444446
self.assertEqual(result.output, expected)
445447
expected_config = config.LintConfig()
446-
expected_config.target = os.path.abspath(os.getcwd())
448+
expected_config.target = os.path.realpath(os.getcwd())
447449
install_hook.assert_called_once_with(expected_config)
448450

449451
@patch('gitlint.hooks.GitHookInstaller.install_commit_msg_hook')
@@ -467,7 +469,7 @@ def test_install_hook_negative(self, install_hook):
467469
self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE)
468470
self.assertEqual(result.output, u"tëst\n")
469471
expected_config = config.LintConfig()
470-
expected_config.target = os.path.abspath(os.getcwd())
472+
expected_config.target = os.path.realpath(os.getcwd())
471473
install_hook.assert_called_once_with(expected_config)
472474

473475
@patch('gitlint.hooks.GitHookInstaller.uninstall_commit_msg_hook')
@@ -479,7 +481,7 @@ def test_uninstall_hook(self, uninstall_hook):
479481
self.assertEqual(result.exit_code, 0)
480482
self.assertEqual(result.output, expected)
481483
expected_config = config.LintConfig()
482-
expected_config.target = os.path.abspath(os.getcwd())
484+
expected_config.target = os.path.realpath(os.getcwd())
483485
uninstall_hook.assert_called_once_with(expected_config)
484486

485487
@patch('gitlint.hooks.GitHookInstaller.uninstall_commit_msg_hook', side_effect=hooks.GitHookInstallerError(u"tëst"))
@@ -489,5 +491,5 @@ def test_uninstall_hook_negative(self, uninstall_hook):
489491
self.assertEqual(result.exit_code, self.GIT_CONTEXT_ERROR_CODE)
490492
self.assertEqual(result.output, u"tëst\n")
491493
expected_config = config.LintConfig()
492-
expected_config.target = os.path.abspath(os.getcwd())
494+
expected_config.target = os.path.realpath(os.getcwd())
493495
uninstall_hook.assert_called_once_with(expected_config)

0 commit comments

Comments
 (0)