Skip to content

Commit

Permalink
Update supported Python and Django versions
Browse files Browse the repository at this point in the history
  • Loading branch information
strayer committed Jan 22, 2022
1 parent 936ed70 commit 72f965d
Show file tree
Hide file tree
Showing 10 changed files with 37 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# py.test
.cache
.pytest_cache

# tox
.tox*
Expand Down Expand Up @@ -27,3 +28,7 @@ test_app/db.sqlite3

# venv
.venv

# direnv
.direnv
.envrc
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python 3.7.12 3.8.12 3.9.10 3.10.2 pypy3.7-7.3.7 pypy3.8-7.3.7
10 changes: 10 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
History
-------

Unreleased
~~~~~~~~~~

* Added support for Python 3.8, 3.9 and 3.10
* Added support for PyPy 3.7 and 3.8
* Added support for Django 2.2 and 3.2
* Dropped suport for now unsupported Python and Django versions:
* Python 2.7, 3.4, 3.5 and 3.6
* Django 1.11, 2.1 and 3.0

0.4.0 (2019-06-27)
~~~~~~~~~~~~~~~~~~

Expand Down
11 changes: 5 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ Install django-gcloud-storage::
pip install django-gcloud-storage

Create a GCS service account JSON keyfile and a bucket for your application.
Check the documentation of google-cloud-python and Google Cloud Platform for
more details:
Check the documentation of Google Cloud Platform for more details:

https://googlecloudplatform.github.io/google-cloud-python/latest/core/auth.html#setting-up-a-service-account
https://cloud.google.com/iam/docs/creating-managing-service-accounts#creating

https://cloud.google.com/storage/docs/authentication#generating-a-private-key
https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating

Update your Django settings and use it like any other Django storage module::

Expand All @@ -49,8 +48,8 @@ Update your Django settings and use it like any other Django storage module::
Features
--------

* Fully tested on Python 2.7, 3.4 - 3.7, PyPy 2.7-7.1.1 and PyPy 3.6-7.1.1 with
Django 1.11 and 2.1 - 2.2
* Fully tested on Python 3.7 - 3.10, PyPy 3.7-7.3.7 and PyPy 3.8-7.3.7 with
Django 2.2 and 3.2
* Files are locally downloaded as SpooledTemporaryFile objects to avoid memory
abuse
* Changed files will automatically be reuploaded to GCS when closed
Expand Down
26 changes: 4 additions & 22 deletions django_gcloud_storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
from tempfile import SpooledTemporaryFile
import mimetypes
import urllib.parse

import django
from django.conf import settings
Expand All @@ -21,15 +22,6 @@

__version__ = '0.4.0'

DJANGO_17 = django.get_version().startswith('1.7.')

try:
# For Python 3.0 and later
from urllib import parse as urlparse
except ImportError:
# Fall back to Python 2's urllib2
import urlparse


def safe_join(base, path):
base = force_text(base).replace("\\", "/").lstrip("/").rstrip("/") + "/"
Expand All @@ -39,7 +31,7 @@ def safe_join(base, path):
if base == "/":
base = ""

resolved_url = urlparse.urljoin(base, path)
resolved_url = urllib.parse.urljoin(base, path)

resolved_url = re.sub("//+", "/", resolved_url)

Expand Down Expand Up @@ -84,13 +76,7 @@ def __init__(self, blob, maxsize=1000):
def _update_blob(self):
# Specify explicit size to avoid problems with not yet spooled temporary files
# Djangos File.size property already knows how to handle cases like this

if DJANGO_17 and self._tmpfile.name is None: # Django bug #22307
size = self._tmpfile.tell()
else:
size = self.size

self._blob.upload_from_file(self._tmpfile, size=size, rewind=True)
self._blob.upload_from_file(self._tmpfile, size=self.size, rewind=True)

def write(self, content):
self._dirty = True
Expand Down Expand Up @@ -228,18 +214,14 @@ def size(self, name):

return blob.size if blob is not None else None

def modified_time(self, name):
def get_modified_time(self, name):
name = safe_join(self.bucket_subdir, name)
name = prepare_name(name)

blob = self.bucket.get_blob(name)

return blob.updated if blob is not None else None

def get_modified_time(self, name):
# In Django>=1.10, modified_time is deprecated, and modified_time will be removed in Django 2.0.
return self.modified_time(name)

def listdir(self, path):
path = safe_join(self.bucket_subdir, path)
path = prepare_name(path)
Expand Down
13 changes: 5 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,23 @@ def get_version(*file_paths):
include_package_data=True,
install_requires=[
"google-cloud-storage>=1.10.0",
"django>=1.11"
"django>=2.2"
],
license="BSD",
zip_safe=False,
keywords='django-gcloud-storage gcloud google-cloud gcs',
classifiers=[
'Development Status :: 3 - Alpha',
'Framework :: Django',
'Framework :: Django :: 1.11',
'Framework :: Django :: 2.1',
'Framework :: Django :: 2.2',
'Framework :: Django :: 3.2',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Natural Language :: English',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
],
)
9 changes: 1 addition & 8 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# coding=utf-8
from __future__ import unicode_literals

import os
import string

Expand Down Expand Up @@ -44,15 +42,10 @@ def storage(request):
)

# Make sure the bucket exists
bucket = Bucket(storage.client, bucket_name)
bucket.create(
location=request.config.getoption("--gcs-bucket-location")
)
storage.client.create_bucket(bucket_name, location=request.config.getoption("--gcs-bucket-location"))

yield storage

storage.bucket.delete_blobs(storage.bucket.list_blobs())

storage.bucket.delete(force=True)

@pytest.fixture(scope="module")
Expand Down
8 changes: 2 additions & 6 deletions tests/test_class.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
# coding=utf-8
from __future__ import unicode_literals

import datetime
import ssl
import sys
from tempfile import TemporaryFile

import google.cloud.exceptions
import pytest
Expand Down Expand Up @@ -134,7 +131,6 @@ def test_should_return_created_time(self, storage, test_file):
assert isinstance(storage.created_time(test_file), datetime.datetime)

def test_should_return_modified_time(self, storage, test_file):
assert isinstance(storage.modified_time(test_file), datetime.datetime)
assert isinstance(storage.get_modified_time(test_file), datetime.datetime)

def test_should_be_able_to_delete_files(self, storage):
Expand Down Expand Up @@ -192,7 +188,7 @@ def test_changed_files_should_be_reuploaded(self, storage):
file_content = "gcloud".encode("ascii")

upload_test_file(storage, file_name, "")
first_modified_time = storage.modified_time(file_name)
first_modified_time = storage.get_modified_time(file_name)
local_tmpfile = storage.open(file_name)

assert local_tmpfile.read() == "".encode("ascii")
Expand All @@ -202,7 +198,7 @@ def test_changed_files_should_be_reuploaded(self, storage):
local_tmpfile.close()

assert storage.open(file_name).read() == file_content
assert storage.modified_time(file_name) != first_modified_time
assert storage.get_modified_time(file_name) != first_modified_time

def test_open_should_be_able_to_create_new_file(self, storage):
file_name = "test_open_creates_file"
Expand Down
8 changes: 1 addition & 7 deletions tests/test_django.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
# coding=utf-8
from __future__ import unicode_literals

import contextlib
import os
import shutil
import tempfile
from tempfile import TemporaryDirectory

import pytest
from django.core.files import File
from django.utils.crypto import get_random_string

from test_app.app.models import ModelWithFileField

try:
from tempfile import TemporaryDirectory
except ImportError:
from setuptools.py31compat import TemporaryDirectory


@contextlib.contextmanager
def make_temp_directory():
Expand Down
9 changes: 3 additions & 6 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
[tox]
envlist =
{py27,py34,py35,py36,py37,pypy2,pypy3}-django111,
{py35,py36,py37,pypy3}-{django21,django22},
{py36,py37,py38,pypy3}-{django3},
{py37,py38,py39,pypy37,pypy38}-{django22},
{py37,py38,py39,pypy37,pypy38}-{django32},
[testenv]
deps =
-rrequirements-test.txt
django111: django<1.12
django21: django<2.2
django22: django<2.3
django3: django==3.0a1
django32: django<3.3
commands = py.test {posargs}
[pytest]
DJANGO_SETTINGS_MODULE = test_app.app.settings
Expand Down

0 comments on commit 72f965d

Please sign in to comment.