Skip to content

Commit 0feb05b

Browse files
committed
Add pre-commit linting
1 parent 9504564 commit 0feb05b

18 files changed

+297
-219
lines changed

Diff for: .editorconfig

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ end_of_line = lf
99
charset = utf-8
1010
trim_trailing_whitespace = true
1111
insert_final_newline = true
12+
max_line_length = 120
1213
indent_style = space
1314
indent_size = 2
1415

Diff for: .flake8

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[flake8]
2+
max-line-length = 120
3+
exclude = .venv,venv
4+
# https://black.readthedocs.io/en/stable/compatible_configs.html#flake8
5+
# W503: line break before binary operator (default black formatting)
6+
# E203: whitespace before ':' (default black formatting)
7+
ignore = S311, W503, E203

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ __pycache__/
99
# Distribution / packaging
1010
.Python
1111
env/
12+
venv/
13+
.venv/
1214
build/
1315
develop-eggs/
1416
dist/

Diff for: .pre-commit-config.yaml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
default_language_version:
2+
python: python3.8
3+
minimum_pre_commit_version: 2.4.0
4+
repos:
5+
- repo: https://github.com/pre-commit/pre-commit-hooks
6+
rev: v4.0.1
7+
hooks:
8+
- id: check-yaml
9+
- id: check-json
10+
- id: check-toml
11+
- id: trailing-whitespace
12+
args: [--markdown-linebreak-ext=md]
13+
- id: end-of-file-fixer
14+
exclude_types: [svg]
15+
exclude: .*\.(min\.js|min\.css)$
16+
17+
- repo: https://github.com/pycqa/isort
18+
rev: 5.8.0
19+
hooks:
20+
- id: isort
21+
22+
- repo: https://github.com/psf/black
23+
rev: 21.5b2
24+
hooks:
25+
- id: black
26+
27+
# Flake8 includes pyflakes, pycodestyle, mccabe, pydocstyle, bandit
28+
- repo: https://gitlab.com/pycqa/flake8
29+
rev: 3.9.2
30+
hooks:
31+
- id: flake8
32+
additional_dependencies: ["flake8-bandit"]
33+
exclude: migrations|settings

Diff for: .travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ dist: focal
66
matrix:
77
include:
88
- python: 3.8
9-
env: TOXENV=isort
9+
env: TOXENV=pre-commit
1010

1111
- python: 3.5
1212
env: TOXENV=py35-2.2

Diff for: csv_export/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '1.1.0'
1+
__version__ = "1.1.0"

Diff for: csv_export/apps.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33

44
class CSVExportViewConfig(AppConfig):
5-
name = 'csv_export'
6-
verbose_name = 'CSV Export View'
5+
name = "csv_export"
6+
verbose_name = "CSV Export View"

Diff for: csv_export/views.py

+43-37
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ def _get_method_type():
1212
class C(object):
1313
def x(self):
1414
pass
15-
return type(getattr(C, 'x'))
15+
16+
return type(getattr(C, "x"))
17+
1618

1719
_method_type = _get_method_type()
1820

@@ -40,36 +42,39 @@ def __init__(self, **kwargs):
4042
if cls == CSVExportView:
4143
# We can stop checking once we hit CSVExportView.
4244
break
43-
if hasattr(cls, 'get_fields') and type(getattr(cls, 'get_fields')) == _method_type:
45+
if hasattr(cls, "get_fields") and isinstance(getattr(cls, "get_fields"), _method_type):
4446
get_fields_overridden = True
4547
break
4648

4749
if not get_fields_overridden:
4850
if not self.fields and not self.exclude:
49-
raise ImproperlyConfigured('\'fields\' or \'exclude\' must be specified.')
51+
raise ImproperlyConfigured("'fields' or 'exclude' must be specified.")
5052

5153
if self.fields and self.exclude:
52-
raise ImproperlyConfigured('Specifying both \'fields\' and \'exclude\' is not permitted.')
54+
raise ImproperlyConfigured("Specifying both 'fields' and 'exclude' is not permitted.")
5355

5456
# Check that some special functions are not being overridden.
55-
for function_override in ('get_context_data', 'get_paginate_by', 'get_allow_empty', 'get_context_object_name'):
56-
if function_override in self.__class__.__dict__ and \
57-
type(self.__class__.__dict__[function_override]) == types.FunctionType:
58-
raise ImproperlyConfigured('Overriding \'{}()\' is not permitted.'.format(function_override))
57+
for function_override in ("get_context_data", "get_paginate_by", "get_allow_empty", "get_context_object_name"):
58+
if function_override in self.__class__.__dict__ and isinstance(
59+
self.__class__.__dict__[function_override], types.FunctionType
60+
):
61+
raise ImproperlyConfigured("Overriding '{}()' is not permitted.".format(function_override))
5962

6063
if self.paginate_by:
61-
raise ImproperlyConfigured('\'{}\' does not support pagination.'.format(self.__class__.__name__))
64+
raise ImproperlyConfigured("'{}' does not support pagination.".format(self.__class__.__name__))
6265

6366
if not self.allow_empty:
64-
raise ImproperlyConfigured('\'{}\' does not support disabling allow_empty.'.format(self.__class__.__name__))
67+
raise ImproperlyConfigured("'{}' does not support disabling allow_empty.".format(self.__class__.__name__))
6568

6669
if self.context_object_name:
67-
raise ImproperlyConfigured('\'{}\' does not support setting context_object_name.'.format(self.__class__.__name__))
70+
raise ImproperlyConfigured(
71+
"'{}' does not support setting context_object_name.".format(self.__class__.__name__)
72+
)
6873

6974
def get_fields(self, queryset):
70-
""" Override if a dynamic fields are required. """
75+
"""Override if a dynamic fields are required."""
7176
field_names = self.fields
72-
if not field_names or field_names == '__all__':
77+
if not field_names or field_names == "__all__":
7378
opts = queryset.model._meta
7479
field_names = [field.name for field in opts.fields]
7580

@@ -81,17 +86,17 @@ def get_fields(self, queryset):
8186
return field_names
8287

8388
def get_filename(self, queryset):
84-
""" Override if a dynamic filename is required. """
89+
"""Override if a dynamic filename is required."""
8590
filename = self.filename
8691
if not filename:
87-
filename = queryset.model._meta.verbose_name_plural.replace(' ', '-')
92+
filename = queryset.model._meta.verbose_name_plural.replace(" ", "-")
8893
return filename
8994

9095
def get_field_value(self, obj, field_name):
91-
""" Override if a custom value or behaviour is required for specific fields. """
92-
if '__' not in field_name:
93-
if hasattr(obj, 'all') and hasattr(obj, 'iterator'):
94-
return ','.join([getattr(ro, field_name) for ro in obj.all()])
96+
"""Override if a custom value or behaviour is required for specific fields."""
97+
if "__" not in field_name:
98+
if hasattr(obj, "all") and hasattr(obj, "iterator"):
99+
return ",".join([getattr(ro, field_name) for ro in obj.all()])
95100

96101
try:
97102
field = obj._meta.get_field(field_name)
@@ -103,58 +108,59 @@ def get_field_value(self, obj, field_name):
103108

104109
value = field.value_from_object(obj)
105110
if field.many_to_many:
106-
return ','.join([force_str(ro) for ro in value])
111+
return ",".join([force_str(ro) for ro in value])
107112
elif field.choices:
108-
if value is None or force_str(value).strip() == '':
109-
return ''
113+
if value is None or force_str(value).strip() == "":
114+
return ""
110115
return dict(field.choices)[value]
111116
return field.value_from_object(obj)
112117
else:
113-
related_field_names = field_name.split('__')
118+
related_field_names = field_name.split("__")
114119
related_obj = getattr(obj, related_field_names[0])
115-
related_field_name = '__'.join(related_field_names[1:])
120+
related_field_name = "__".join(related_field_names[1:])
116121
return self.get_field_value(related_obj, related_field_name)
117122

118123
def get_header_name(self, model, field_name):
119-
""" Override if a custom value or behaviour is required for specific fields. """
120-
if '__' not in field_name:
124+
"""Override if a custom value or behaviour is required for specific fields."""
125+
if "__" not in field_name:
121126
try:
122127
field = model._meta.get_field(field_name)
123128
except FieldDoesNotExist as e:
124129
if not hasattr(model, field_name):
125130
raise e
126131
# field_name is a property.
127-
return field_name.replace('_', ' ').title()
132+
return field_name.replace("_", " ").title()
128133

129134
return force_str(field.verbose_name).title()
130135
else:
131-
related_field_names = field_name.split('__')
136+
related_field_names = field_name.split("__")
132137
field = model._meta.get_field(related_field_names[0])
133-
assert field.is_relation
134-
return self.get_header_name(field.related_model, '__'.join(related_field_names[1:]))
138+
if not field.is_relation:
139+
raise ImproperlyConfigured(f"{field} is not a relation")
140+
return self.get_header_name(field.related_model, "__".join(related_field_names[1:]))
135141

136142
def get_csv_writer_fmtparams(self):
137143
return {
138-
'dialect': 'excel',
139-
'quoting': csv.QUOTE_ALL,
144+
"dialect": "excel",
145+
"quoting": csv.QUOTE_ALL,
140146
}
141147

142148
def get(self, request, *args, **kwargs):
143149
queryset = self.get_queryset()
144150

145151
field_names = self.get_fields(queryset)
146152

147-
response = HttpResponse(content_type='text/csv')
153+
response = HttpResponse(content_type="text/csv")
148154

149155
filename = self.get_filename(queryset)
150-
if not filename.endswith('.csv'):
151-
filename += '.csv'
152-
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
156+
if not filename.endswith(".csv"):
157+
filename += ".csv"
158+
response["Content-Disposition"] = 'attachment; filename="{}"'.format(filename)
153159

154160
writer = csv.writer(response, **self.get_csv_writer_fmtparams())
155161

156162
if self.specify_separator:
157-
response.write('sep={}{}'.format(writer.dialect.delimiter, writer.dialect.lineterminator))
163+
response.write("sep={}{}".format(writer.dialect.delimiter, writer.dialect.lineterminator))
158164

159165
if self.header:
160166
writer.writerow([self.get_header_name(queryset.model, field_name) for field_name in list(field_names)])

Diff for: pyproject.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# https://black.readthedocs.io/en/stable/compatible_configs.html#isort
2+
[tool.isort]
3+
multi_line_output = 3
4+
include_trailing_comma = true
5+
force_grid_wrap = 0
6+
use_parentheses = true
7+
ensure_newline_before_comments = true
8+
line_length = 120
9+
10+
[tool.black]
11+
line_length = 120

Diff for: runtests.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
from django.conf import settings
77
from django.test.utils import get_runner
88

9-
if __name__ == '__main__':
10-
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings'
9+
if __name__ == "__main__":
10+
os.environ["DJANGO_SETTINGS_MODULE"] = "tests.test_settings"
1111
django.setup()
1212
TestRunner = get_runner(settings)
1313
test_runner = TestRunner()
14-
failures = test_runner.run_tests(['tests'])
14+
failures = test_runner.run_tests(["tests"])
1515
sys.exit(bool(failures))

Diff for: setup.py

+35-38
Original file line numberDiff line numberDiff line change
@@ -11,62 +11,59 @@
1111

1212
def get_long_description():
1313
current_dir = path.abspath(path.dirname(__file__))
14-
readme_path = path.join(current_dir, 'README.md')
15-
with open(readme_path, encoding='utf-8') as f:
14+
readme_path = path.join(current_dir, "README.md")
15+
with open(readme_path, encoding="utf-8") as f:
1616
return f.read()
1717

1818

1919
def get_version():
20-
version_file = open(path.join('csv_export', '__init__.py'), encoding='utf-8').read()
21-
version_match = re.search('__version__ = [\'"]([0-9]+\.[0-9]+\.[0-9]+)[\'"]', version_file, re.MULTILINE)
20+
version_file = open(path.join("csv_export", "__init__.py"), encoding="utf-8").read()
21+
version_match = re.search(r"__version__ = ['\"]([0-9]+\.[0-9]+\.[0-9]+)['\"]", version_file, re.MULTILINE)
2222
if version_match:
2323
return version_match.group(1)
24-
raise RuntimeError('Unable to find version string.')
24+
raise RuntimeError("Unable to find version string.")
2525

2626

27-
if sys.argv[-1] == 'publish':
28-
os.system('python setup.py sdist upload')
29-
print('You should also add a git tag for this version:')
30-
print(' git tag {}'.format(get_version()))
31-
print(' git push --tags')
27+
if sys.argv[-1] == "publish":
28+
os.system("python setup.py sdist upload") # noqa
29+
print("You should also add a git tag for this version:")
30+
print(" git tag {}".format(get_version()))
31+
print(" git push --tags")
3232
sys.exit()
3333

3434

3535
setup(
36-
name='django-csv-export-view',
36+
name="django-csv-export-view",
3737
version=get_version(),
38-
license='BSD',
39-
description='Django class-based view for CSV exports',
38+
license="BSD",
39+
description="Django class-based view for CSV exports",
4040
long_description=get_long_description(),
41-
long_description_content_type='text/markdown',
42-
url='https://github.com/benkonrath/django-csv-export-view',
43-
44-
author='Ben Konrath',
45-
author_email='[email protected]',
46-
47-
packages=find_packages(exclude=['tests*']),
41+
long_description_content_type="text/markdown",
42+
url="https://github.com/benkonrath/django-csv-export-view",
43+
author="Ben Konrath",
44+
author_email="[email protected]",
45+
packages=find_packages(exclude=["tests*"]),
4846
include_package_data=True,
49-
data_files=[('', ['README.md', 'CHANGELOG.md'])],
47+
data_files=[("", ["README.md", "CHANGELOG.md"])],
5048
zip_safe=False,
5149
install_requires=[
52-
'django>=2.0',
50+
"django>=2.0",
5351
],
54-
python_requires='!=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4',
55-
52+
python_requires="!=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4",
5653
classifiers=[
57-
'Development Status :: 5 - Production/Stable',
58-
'Environment :: Web Environment',
59-
'Framework :: Django',
60-
'Intended Audience :: Developers',
61-
'License :: OSI Approved :: BSD License',
62-
'Operating System :: OS Independent',
63-
'Programming Language :: Python',
64-
'Programming Language :: Python :: 2.7',
65-
'Programming Language :: Python :: 3.5',
66-
'Programming Language :: Python :: 3.6',
67-
'Programming Language :: Python :: 3.7',
68-
'Programming Language :: Python :: 3.8',
69-
'Programming Language :: Python :: 3.9',
70-
'Topic :: Utilities',
54+
"Development Status :: 5 - Production/Stable",
55+
"Environment :: Web Environment",
56+
"Framework :: Django",
57+
"Intended Audience :: Developers",
58+
"License :: OSI Approved :: BSD License",
59+
"Operating System :: OS Independent",
60+
"Programming Language :: Python",
61+
"Programming Language :: Python :: 2.7",
62+
"Programming Language :: Python :: 3.5",
63+
"Programming Language :: Python :: 3.6",
64+
"Programming Language :: Python :: 3.7",
65+
"Programming Language :: Python :: 3.8",
66+
"Programming Language :: Python :: 3.9",
67+
"Topic :: Utilities",
7168
],
7269
)

Diff for: tests/admin.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
@admin.register(Car)
99
class CarAdmin(admin.ModelAdmin):
10-
actions = ('export_car_csv',)
10+
actions = ("export_car_csv",)
1111

1212
def export_car_csv(self, request, queryset):
13-
view = CSVExportView(queryset=queryset, fields='__all__')
13+
view = CSVExportView(queryset=queryset, fields="__all__")
1414
return view.get(request)
1515

16-
export_car_csv.short_description = 'Export CSV for selected Car records'
16+
export_car_csv.short_description = "Export CSV for selected Car records"

Diff for: tests/models.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
class FieldTest(models.Model):
55
date = models.DateField()
66
datetime = models.DateTimeField()
7-
choice = models.CharField(max_length=1, choices=(('R', 'Red'), ('G', 'Green')), default='R')
8-
empty_choice = models.CharField(max_length=1, choices=(('Y', 'Yellow'), ('B', 'Black')), blank=True)
9-
integer_choice = models.IntegerField(choices=((0, 'Zero'), (1, 'One')), default=0)
7+
choice = models.CharField(max_length=1, choices=(("R", "Red"), ("G", "Green")), default="R")
8+
empty_choice = models.CharField(max_length=1, choices=(("Y", "Yellow"), ("B", "Black")), blank=True)
9+
integer_choice = models.IntegerField(choices=((0, "Zero"), (1, "One")), default=0)
1010

1111
@property
1212
def my_property(self):
13-
return 'Foo'
13+
return "Foo"
1414

1515

1616
# Many-to-one relationships

0 commit comments

Comments
 (0)