Skip to content

Commit 89a4874

Browse files
committed
Update packaging and CI suite
1 parent c0d2641 commit 89a4874

17 files changed

+408
-236
lines changed

.checks.yml

-3
This file was deleted.

.github/FUNDING.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
github: codingjoe
12
custom: https://www.paypal.me/codingjoe

.github/dependabot.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: pip
4+
directory: "/"
5+
schedule:
6+
interval: daily
7+
- package-ecosystem: github-actions
8+
directory: "/"
9+
schedule:
10+
interval: daily

.github/workflows/ci.yml

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
schedule:
9+
- cron: '0 0 * * *'
10+
11+
jobs:
12+
13+
lint:
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
lint-command:
18+
- "bandit dynamic_filenames.py"
19+
- "black --check --diff ."
20+
- "flake8 ."
21+
- "isort --check-only --diff ."
22+
- "pydocstyle ."
23+
runs-on: ubuntu-latest
24+
steps:
25+
- uses: actions/[email protected]
26+
- uses: actions/[email protected]
27+
- uses: actions/[email protected]
28+
with:
29+
path: ~/.cache/pip
30+
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
31+
restore-keys: |
32+
${{ runner.os }}-pip-
33+
- run: python -m pip install -r requirements.txt
34+
- run: ${{ matrix.lint-command }}
35+
36+
readme:
37+
runs-on: ubuntu-latest
38+
steps:
39+
- uses: actions/[email protected]
40+
- uses: actions/[email protected]
41+
- name: Install Python dependencies
42+
run: python -m pip install --upgrade pip setuptools wheel twine readme-renderer
43+
- run: python setup.py sdist bdist_wheel
44+
- run: python -m twine check dist/*
45+
- uses: actions/upload-artifact@v2
46+
with:
47+
path: dist/*
48+
49+
pytest:
50+
runs-on: ubuntu-latest
51+
needs:
52+
- readme
53+
- lint
54+
strategy:
55+
matrix:
56+
python-version:
57+
- "3.7"
58+
- "3.8"
59+
- "3.9"
60+
django-version:
61+
- "2.2.0"
62+
- "3.1.0"
63+
- "3.2.0"
64+
65+
steps:
66+
- name: Set up Python ${{ matrix.python-version }}
67+
uses: actions/[email protected]
68+
with:
69+
python-version: ${{ matrix.python-version }}
70+
- uses: actions/[email protected]
71+
- name: Upgrade Python setuptools
72+
run: python -m pip install --upgrade pip setuptools wheel codecov
73+
- name: Install Django ${{ matrix.django-version }}
74+
run: python -m pip install "django~=${{ matrix.django-version }}"
75+
- run: python setup.py develop
76+
- name: Run tests
77+
run: python setup.py test
78+
- run: codecov
79+
80+
extras:
81+
needs:
82+
- readme
83+
- lint
84+
runs-on: ubuntu-latest
85+
strategy:
86+
matrix:
87+
extras:
88+
- slugify
89+
90+
steps:
91+
- uses: actions/[email protected]
92+
- uses: actions/[email protected]
93+
- name: Install Python dependencies
94+
run: |
95+
python -m pip install --upgrade pip setuptools wheel codecov
96+
python -m pip install -e .[${{ matrix.extras }}]
97+
- name: Run tests
98+
run: python setup.py test
99+
- run: codecov

.github/workflows/release.yml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Release
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
PyPI:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/[email protected]
12+
- uses: actions/[email protected]
13+
- name: Install Python dependencies
14+
run: python -m pip install --upgrade pip setuptools wheel twine
15+
- name: Build dist packages
16+
run: python setup.py sdist bdist_wheel
17+
- name: Upload packages
18+
run: python -m twine upload dist/*
19+
env:
20+
TWINE_USERNAME: __token__
21+
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}

.travis.yml

-22
This file was deleted.

dynamic_filenames.py

+32-27
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,53 @@
88
def slugify(value):
99
try: # use unicode-slugify library if installed
1010
from slugify import slugify as _slugify
11+
1112
return _slugify(value, only_ascii=True)
1213
except ImportError:
1314
from django.utils.text import slugify as _slugify
15+
1416
return _slugify(value, allow_unicode=False)
1517

1618

1719
class SlugFormatter(Formatter):
18-
format_spec_pattern = re.compile(r'(\.\d+)?([\d\w]+)?')
20+
format_spec_pattern = re.compile(r"(\.\d+)?([\d\w]+)?")
1921

2022
def format_field(self, value, format_spec):
2123
precision, ftype = self.format_spec_pattern.match(format_spec).groups()
2224
if precision:
23-
precision = int(precision.lstrip('.'))
24-
if ftype == 'slug':
25+
precision = int(precision.lstrip("."))
26+
if ftype == "slug":
2527
return slugify(value)[:precision]
2628
return super().format_field(value=value, format_spec=format_spec)
2729

2830

2931
class ExtendedUUID(uuid.UUID):
30-
format_spec_pattern = re.compile(r'(\.\d+)?([\d\w]+)?')
32+
format_spec_pattern = re.compile(r"(\.\d+)?([\d\w]+)?")
3133

3234
def __format__(self, format_spec):
3335
precision, ftype = self.format_spec_pattern.match(format_spec).groups()
3436
if precision:
35-
precision = int(precision.lstrip('.'))
36-
if ftype == '':
37+
precision = int(precision.lstrip("."))
38+
if ftype == "":
3739
return str(self)[:precision]
38-
if ftype == 's':
40+
if ftype == "s":
3941
return str(self)[:precision]
40-
if ftype == 'i':
42+
if ftype == "i":
4143
return str(self.int)[:precision]
42-
if ftype == 'x':
44+
if ftype == "x":
4345
return self.hex.lower()[:precision]
44-
if ftype == 'X':
46+
if ftype == "X":
4547
return self.hex.upper()[:precision]
46-
if ftype == 'base32':
47-
return base64.b32encode(
48-
self.bytes
49-
).decode('utf-8').rstrip('=\n')[:precision]
50-
if ftype == 'base64':
51-
return base64.urlsafe_b64encode(
52-
self.bytes
53-
).decode('utf-8').rstrip('=\n')[:precision]
48+
if ftype == "base32":
49+
return (
50+
base64.b32encode(self.bytes).decode("utf-8").rstrip("=\n")[:precision]
51+
)
52+
if ftype == "base64":
53+
return (
54+
base64.urlsafe_b64encode(self.bytes)
55+
.decode("utf-8")
56+
.rstrip("=\n")[:precision]
57+
)
5458
return super().__format__(format_spec)
5559

5660

@@ -71,7 +75,6 @@ class FileModel(models.Model):
7175
my_file = models.FileField(upload_to=upload_to_pattern)
7276
7377
Args:
74-
7578
ext: File extension including the dot.
7679
name: Filename excluding the folders.
7780
model_name: Name of the Django model.
@@ -103,28 +106,30 @@ class FileModel(models.Model):
103106

104107
formatter = SlugFormatter()
105108

106-
filename_pattern = '{name}{ext}'
109+
filename_pattern = "{name}{ext}"
107110

108111
def __call__(self, instance, filename):
109112
"""Return filename based for given instance and filename."""
110113
# UUID needs to be set on call, not per instance to avoid state leakage.
111114
path, ext = os.path.splitext(filename)
112115
path, name = os.path.split(path)
113116
defaults = {
114-
'ext': ext,
115-
'name': name,
116-
'model_name': instance._meta.model_name,
117-
'app_label': instance._meta.app_label,
118-
'uuid': self.get_uuid(),
119-
'instance': instance,
117+
"ext": ext,
118+
"name": name,
119+
"model_name": instance._meta.model_name,
120+
"app_label": instance._meta.app_label,
121+
"uuid": self.get_uuid(),
122+
"instance": instance,
120123
}
121124
defaults.update(self.override_values)
122125
return self.formatter.format(self.filename_pattern, **defaults)
123126

124127
def __init__(self, **kwargs):
125128
self.kwargs = kwargs
126129
override_values = kwargs.copy()
127-
self.filename_pattern = override_values.pop('filename_pattern', self.filename_pattern)
130+
self.filename_pattern = override_values.pop(
131+
"filename_pattern", self.filename_pattern
132+
)
128133
self.override_values = override_values
129134

130135
def deconstruct(self):

requirements-dev.txt

-4
This file was deleted.

requirements.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
Django>=1.11
1+
bandit==1.7.0
2+
black==21.9b0
3+
flake8==3.9.2
4+
isort==5.9.3
5+
pydocstyle==6.1.1

setup.cfg

+46-24
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,80 @@
11
[metadata]
22
name = django-dynamic-filenames
33
author = Johannes Hoppe
4-
author-email = [email protected]
5-
summary = Write advanced filename patterns using the Format String Syntax.
4+
author_email = [email protected]
5+
description = Write advanced filename patterns using the Format String Syntax.
66
long_description = file: README.rst
77
long_description_content_type = text/x-rst
8-
home-page = https://github.com/codingjoe/django-dynamic-filenames
9-
project_urls =
10-
Bug Tracker = https://github.com/codingjoe/django-dynamic-filenames/issues
11-
Documentation = https://github.com/codingjoe/django-dynamic-filenames
12-
Source Code = https://github.com/codingjoe/django-dynamic-filenames
138
license = MIT License
9+
license_file = LICENSE
10+
url = https://github.com/codingjoe/django-dynamic-filenames
1411
classifier =
1512
Development Status :: 5 - Production/Stable
1613
Environment :: Web Environment
1714
Intended Audience :: Developers
1815
License :: OSI Approved :: MIT License
1916
Operating System :: OS Independent
17+
Framework :: Django
18+
Framework :: Django :: 2.2
19+
Framework :: Django :: 3.1
20+
Framework :: Django :: 3.2
2021
Programming Language :: Python
2122
Programming Language :: Python :: 3
22-
Framework :: Django
23+
Programming Language :: Python :: 3.7
24+
Programming Language :: Python :: 3.8
25+
Programming Language :: Python :: 3.9
2326
keywords =
2427
django
2528
django-storages
2629
file
2730

28-
[extras]
31+
[options]
32+
install_requires =
33+
django>=2.2
34+
setup_requires =
35+
setuptools_scm
36+
pytest-runner
37+
tests_require =
38+
pytest
39+
pytest-cov
40+
pytest-django
41+
42+
[options.extras_require]
2943
slugify =
3044
unicode-slugify
3145

32-
[pbr]
33-
skip_authors = true
34-
skip_changelog = true
46+
[aliases]
47+
test = pytest
48+
49+
[bdist_wheel]
50+
universal = 1
3551

3652
[tool:pytest]
3753
norecursedirs = env docs .eggs
38-
addopts = --tb=short -rxs
54+
addopts = --cov=dynamic_filenames --tb=short -rxs
3955
DJANGO_SETTINGS_MODULE=tests.testapp.settings
4056

41-
[flake8]
42-
max-line-length = 99
43-
max-complexity = 10
44-
statistics = true
45-
show-source = true
57+
[coverage:run]
58+
source = dynamic_filenames
59+
60+
[coverage:report]
61+
show_missing = True
62+
skip_covered = True
4663

4764
[pydocstyle]
48-
add-ignore = D1
49-
match-dir = (?!tests|env|docs|\.).*
65+
add_ignore = D1
66+
match_dir = (?!tests|env|docs|\.).*
67+
match = (?!setup).*.py
68+
69+
[flake8]
70+
max-line-length=88
71+
select = C,E,F,W,B,B950
72+
ignore = E203, E501, W503, E731
5073

5174
[isort]
5275
atomic = true
53-
multi_line_output = 5
54-
line_length = 79
55-
skip = manage.py,docs,.tox,env
76+
line_length = 88
5677
known_first_party = dynamic_filenames, tests
57-
known_third_party = django, slugify
78+
include_trailing_comma = True
79+
default_section=THIRDPARTY
5880
combine_as_imports = true

0 commit comments

Comments
 (0)