Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v4
Expand All @@ -21,7 +21,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions black==22.12.0 isort==5.12.0 ruff==0.0.270
pip install tox tox-gh-actions black==26.1.0 isort==7.0.0 ruff==0.14.14

- name: Lint
run: ./lint.sh
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Based upon django-database-files by [Kimetrica](https://github.com/kimetrica/dja

Requires:

* Django 2.2 - 4.0
* Django 4.2+

Installation
------------
Expand Down Expand Up @@ -103,7 +103,7 @@ Code should be linted with:

Tests require the Python development headers to be installed, which you can install on Ubuntu with:

sudo apt-get install python3.12-minimal python3.12-dev
sudo apt-get install python3.14-minimal python3.14-dev

To run unittests across multiple Python versions, install:

Expand All @@ -126,4 +126,3 @@ To build and deploy a versioned package to PyPI, verify [all unittests are passi
python setup.py sdist bdist_wheel
twine check dist/*
twine upload dist/*

7 changes: 5 additions & 2 deletions binary_database_files/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ class DatabaseFilesAppConfig(AppConfig):
@register()
def check_settings(app_configs, **kwargs):
errors = []
if not settings.MEDIA_ROOT and settings.DATABASE_FILES_URL_METHOD_NAME == "URL_METHOD_1":
if (
not settings.MEDIA_ROOT
and settings.DATABASE_FILES_URL_METHOD_NAME == "URL_METHOD_1"
):
errors.append(
Error(
"MEDIA_ROOT is not defined, yet you are using URL_METHOD_1 which serves media files from the filesystem.",
Expand All @@ -30,4 +33,4 @@ def check_settings(app_configs, **kwargs):
id="binary_database_files.E002",
)
)
return errors
return errors
2 changes: 1 addition & 1 deletion binary_database_files/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def dump_files(cls, debug=True, verbose=False):
if verbose:
print("Checking %i total files..." % (total,))
i = 0
for (file_id, name, content_hash) in q.iterator():
for file_id, name, content_hash in q.iterator():
i += 1
if verbose and not i % 100:
print("%i of %i" % (i, total))
Expand Down
12 changes: 6 additions & 6 deletions binary_database_files/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ def URL_METHOD_2(name):

DATABASE_FILES_URL_METHOD = settings.DATABASE_FILES_URL_METHOD = method

DB_FILES_DEFAULT_ENFORCE_ENCODING = (
settings.DB_FILES_DEFAULT_ENFORCE_ENCODING
) = getattr(settings, "DB_FILES_DEFAULT_ENFORCE_ENCODING", True)
DB_FILES_DEFAULT_ENFORCE_ENCODING = settings.DB_FILES_DEFAULT_ENFORCE_ENCODING = (
getattr(settings, "DB_FILES_DEFAULT_ENFORCE_ENCODING", True)
)

DB_FILES_DEFAULT_ENCODING = settings.DB_FILES_DEFAULT_ENCODING = getattr(
settings, "DB_FILES_DEFAULT_ENCODING", "ascii"
Expand All @@ -52,8 +52,8 @@ def URL_METHOD_2(name):
settings, "DB_FILES_DEFAULT_ERROR_METHOD", "ignore"
)

DB_FILES_DEFAULT_HASH_FN_TEMPLATE = (
settings.DB_FILES_DEFAULT_HASH_FN_TEMPLATE
) = getattr(settings, "DB_FILES_DEFAULT_HASH_FN_TEMPLATE", "%s.hash")
DB_FILES_DEFAULT_HASH_FN_TEMPLATE = settings.DB_FILES_DEFAULT_HASH_FN_TEMPLATE = (
getattr(settings, "DB_FILES_DEFAULT_HASH_FN_TEMPLATE", "%s.hash")
)

DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
1 change: 1 addition & 0 deletions binary_database_files/storage.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Custom storage backend that stores files in the database to facilitate scaling."""

import os
from io import BytesIO, UnsupportedOperation

Expand Down
10 changes: 5 additions & 5 deletions binary_database_files/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,17 +434,17 @@ def test_serve_file_from_database(self):
self.assertEqual(response["content-length"], "10")

@override_settings(
MEDIA_ROOT=global_settings.MEDIA_ROOT,
DATABASE_FILES_URL_METHOD_NAME = "URL_METHOD_1", # default
DB_FILES_AUTO_EXPORT_DB_TO_FS = True, # default
MEDIA_ROOT=global_settings.MEDIA_ROOT,
DATABASE_FILES_URL_METHOD_NAME="URL_METHOD_1", # default
DB_FILES_AUTO_EXPORT_DB_TO_FS=True, # default
)
def test_refuse_unset_media_root(self):
# regression test for issue #65 where unset MEDIA_ROOT would result in serving the source code

message_a = "(binary_database_files.E001) MEDIA_ROOT is not defined, yet you are using URL_METHOD_1 which serves media files from the filesystem"
with self.assertRaisesMessage(SystemCheckError, message_a):
call_command("check")
call_command("check")

message_b = "(binary_database_files.E002) MEDIA_ROOT is not defined, yet you are using DB_FILES_AUTO_EXPORT_DB_TO_FS which copies media files from the filesystem."
with self.assertRaisesMessage(SystemCheckError, message_b):
call_command("check")
call_command("check")
6 changes: 3 additions & 3 deletions binary_database_files/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def create_directory_for_file(full_path):
else:
os.makedirs(directory, exist_ok=True)
except FileExistsError:
raise FileExistsError('%s exists and is not a directory.' % directory)
raise FileExistsError("%s exists and is not a directory." % directory)


def write_file(name, content, overwrite=False):
Expand Down Expand Up @@ -110,7 +110,7 @@ def write_file(name, content, overwrite=False):
shutil.chown(fqfn, user=uname, group=gname)
shutil.chown(hash_fn, user=uname, group=gname)
except OSError as e:
logger.warning('Could not chown file %s: %s', fqfn, e)
logger.warning("Could not chown file %s: %s", fqfn, e)

# Set permissions.
perms = getattr(settings, "DATABASE_FILES_PERMS", settings.FILE_UPLOAD_PERMISSIONS)
Expand All @@ -119,7 +119,7 @@ def write_file(name, content, overwrite=False):
os.chmod(fqfn, perms)
os.chmod(hash_fn, perms)
except OSError as e:
logger.warning('Could not chmod file %s: %s', fqfn, e)
logger.warning("Could not chmod file %s: %s", fqfn, e)


def get_file_hash(fin, force_encoding=None, encoding=None, errors=None, chunk_size=128):
Expand Down
2 changes: 1 addition & 1 deletion lint.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
ruff binary_database_files
ruff check binary_database_files
black --check binary_database_files
isort --check binary_database_files
2 changes: 1 addition & 1 deletion pip-requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Django>=2.2,<6
Django>=2.2,<=6.0
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.ruff]
line-length = 119 # Allow longer lines for ruff than black and isort, for comments and docstrings
target-version = 'py310'
target-version = 'py314'
exclude = [
'.eggs', # exclude a few common directories in the
'.git', # root of the project
Expand All @@ -18,16 +18,16 @@ exclude = [
'dist',
]

[tool.ruff.mccabe]
[lint.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 10

[tool.ruff.flake8-quotes]
[lint.flake8-quotes]
docstring-quotes = "double"

[tool.black]
line-length = 88
target-version = ['py310']
target-version = ['py314']
include = '\.pyi?$'
exclude = '''
(
Expand Down
9 changes: 4 additions & 5 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,29 @@ ignore = W503, E203 # See https://github.com/PyCQA/pycodestyle/issues/373
max-line-length=160

[tox]
envlist = py{38,39,310,311,312}-django{42},py{310,311,312}-django{50},py{310,311,312,313}-django{51,52}
envlist = py{310,311,312}-django{42},py{310,311,312}-django{50},py{310,311,312,313}-django{51,52},py{314}-django{60}
recreate = True

[gh-actions]
python =
3.8: py38
3.9: py39
3.10: py310
3.11: py311
3.12: py312
3.13: py313
3.14: py314

[testenv]
basepython =
py38: python3.8
py39: python3.9
py310: python3.10
py311: python3.11
py312: python3.12
py313: python3.13
py314: python3.14
deps =
-r{toxinidir}/pip-requirements-test.txt
django42: Django==4.2.*
django50: Django==5.0.*
django51: Django==5.1.*
django52: Django==5.2.*
django60: Django==6.0.*
commands = django-admin test --traceback --pythonpath=. --settings=binary_database_files.tests.settings binary_database_files.tests.tests.DatabaseFilesTestCase{env:TESTNAME:}