Skip to content

Commit

Permalink
Python 3 support
Browse files Browse the repository at this point in the history
  • Loading branch information
rafales committed Feb 13, 2013
1 parent 794bd23 commit 0723bc6
Show file tree
Hide file tree
Showing 19 changed files with 129 additions and 67 deletions.
17 changes: 11 additions & 6 deletions pytest_django/db_reuse.py
Expand Up @@ -3,8 +3,9 @@
The code in this module is heavily inspired by django-nose:
https://github.com/jbalogh/django-nose/
"""
import sys
import types

import new
import py


Expand All @@ -27,7 +28,7 @@ def test_database_exists_from_previous_run(connection):
try:
connection.cursor()
return True
except StandardError: # TODO: Be more discerning but still DB agnostic.
except Exception: # TODO: Be more discerning but still DB agnostic.
return False
finally:
connection.close()
Expand All @@ -47,8 +48,8 @@ def create_test_db(self, verbosity=1, autoclobber=False):
test_db_repr = ''
if verbosity >= 2:
test_db_repr = " ('%s')" % test_database_name
print "Re-using existing test database for alias '%s'%s..." % (
self.connection.alias, test_db_repr)
print("Re-using existing test database for alias '%s'%s..." % (
self.connection.alias, test_db_repr))

# confirm() is not needed/available in Django >= 1.5
# See https://code.djangoproject.com/ticket/17760
Expand All @@ -69,5 +70,9 @@ def monkey_patch_creation_for_db_reuse():
# Make sure our monkey patch is still valid in the future
assert hasattr(creation, 'create_test_db')

creation.create_test_db = new.instancemethod(
create_test_db, creation, creation.__class__)
if sys.version_info < (3, 0):
creation.create_test_db = types.MethodType(
create_test_db, creation, creation.__class__)
else:
creation.create_test_db = types.MethodType(create_test_db,
creation)
10 changes: 8 additions & 2 deletions pytest_django/django_compat.py
@@ -1,5 +1,6 @@
# Note that all functions here assume django is available. So ensure
# this is the case before you call them.
import sys


def is_django_unittest(item):
Expand All @@ -9,5 +10,10 @@ def is_django_unittest(item):
except ImportError:
from django.test import TestCase

return (hasattr(item.obj, 'im_class') and
issubclass(item.obj.im_class, TestCase))
if sys.version_info < (3, 0):
return (hasattr(item.obj, 'im_class') and
issubclass(item.obj.im_class, TestCase))

return (hasattr(item.obj, '__self__') and
hasattr(item.obj.__self__, '__class__') and
issubclass(item.obj.__self__.__class__, TestCase))
26 changes: 16 additions & 10 deletions pytest_django/live_server_helper.py
@@ -1,5 +1,5 @@
import pytest

import sys
import py

def supported():
import django.test.testcases
Expand Down Expand Up @@ -49,15 +49,21 @@ def stop(self):
def url(self):
return 'http://%s:%s' % (self.thread.host, self.thread.port)

def __unicode__(self):
return self.url
if sys.version_info < (3, 0):
def __unicode__(self):
return self.url

def __repr__(self):
return '<LiveServer listening at %s>' % unicode(self)
def __add__(self, other):
return unicode(self) + other
else:
def __str__(self):
return self.url

def __add__(self, other):
# Support string concatenation
return unicode(self) + other
def __add__(self, other):
return str(self) + other

def __repr__(self):
return '<LiveServer listening at %s>' % self.url


def parse_addr(specified_address):
Expand All @@ -73,7 +79,7 @@ def parse_addr(specified_address):
host, port_ranges = specified_address.split(':')
for port_range in port_ranges.split(','):
# A port range can be of either form: '8000' or '8000-8010'.
extremes = map(int, port_range.split('-'))
extremes = list(map(int, port_range.split('-')))
assert len(extremes) in [1, 2]
if len(extremes) == 1:
# Port range of the form '8000'
Expand Down
4 changes: 3 additions & 1 deletion pytest_django/plugin.py
Expand Up @@ -5,6 +5,7 @@
"""

import os
import sys

import pytest

Expand Down Expand Up @@ -70,7 +71,8 @@ def pytest_configure(config):
from django.conf import settings
try:
settings.DATABASES
except ImportError, e:
except ImportError:
e = sys.exc_info()[1]
raise pytest.UsageError(*e.args)

# Register the marks
Expand Down
4 changes: 3 additions & 1 deletion setup.py
Expand Up @@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-

import os
import codecs
from setuptools import setup


Expand All @@ -10,7 +11,8 @@
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
file_path = os.path.join(os.path.dirname(__file__), fname)
return codecs.open(file_path, encoding='utf-8').read()


setup(
Expand Down
10 changes: 10 additions & 0 deletions tests/compat.py
@@ -0,0 +1,10 @@
try:
from django.utils.encoding import force_text
except ImportError:
from django.utils.encoding import force_unicode as force_text


try:
from urllib2 import urlopen
except ImportError:
from urllib.request import urlopen
4 changes: 1 addition & 3 deletions tests/conftest.py
Expand Up @@ -13,8 +13,6 @@
from django.conf import settings




from .db_helpers import create_empty_production_database, get_db_engine, DB_NAME


Expand Down Expand Up @@ -48,7 +46,7 @@ def django_testdir(testdir, monkeypatch):
app_source = TESTS_DIR.dirpath('app')

# Copy the test app to make it available in the new test run
shutil.copytree(unicode(app_source), unicode(tpkg_path.join('app')))
shutil.copytree(py.builtin._totext(app_source), py.builtin._totext(tpkg_path.join('app')))
tpkg_path.join("db_test_settings.py").write(test_settings)

monkeypatch.setenv('DJANGO_SETTINGS_MODULE', 'tpkg.db_test_settings')
Expand Down
33 changes: 23 additions & 10 deletions tests/db_helpers.py
@@ -1,5 +1,5 @@
import subprocess

from .compat import force_text

DB_NAME = 'pytest_django_db_test'
TEST_DB_NAME = 'test_' + DB_NAME
Expand All @@ -24,17 +24,29 @@ def run_cmd(*args):
return CmdResult(ret, stdoutdata, stderrdata)


def run_mysql(*args):
from django.conf import settings
user = settings.DATABASES['default'].get('USER', None)
if user:
args = ('-u', user) + tuple(args)
args = ('mysql',) + tuple(args)
return run_cmd(*args)


def create_empty_production_database():
drop_database(name=DB_NAME)

if get_db_engine() == 'postgresql_psycopg2':
r = run_cmd('psql', 'postgres', '-c', 'CREATE DATABASE %s' % DB_NAME)
assert 'CREATE DATABASE' in r.std_out or 'already exists' in r.std_err
assert ('CREATE DATABASE' in force_text(r.std_out) or
'already exists' in force_text(r.std_err))
return

if get_db_engine() == 'mysql':
r = run_cmd('mysql', '-e', 'CREATE DATABASE %s' % DB_NAME)
assert r.status_code == 0 or 'database exists' in r.std_out
r = run_mysql('-e', 'CREATE DATABASE %s' % DB_NAME)
assert (r.status_code == 0 or
'database exists' in force_text(r.std_out) or
'database exists' in force_text(r.std_err))
return

raise AssertionError('%s cannot be tested properly' % get_db_engine())
Expand All @@ -43,12 +55,13 @@ def create_empty_production_database():
def drop_database(name=TEST_DB_NAME):
if get_db_engine() == 'postgresql_psycopg2':
r = run_cmd('psql', 'postgres', '-c', 'DROP DATABASE %s' % name)
assert 'DROP DATABASE' in r.std_out or 'does not exist' in r.std_err
assert ('DROP DATABASE' in force_text(r.std_out) or
'does not exist' in force_text(r.std_err))
return

if get_db_engine() == 'mysql':
r = run_cmd('mysql', '-u', 'root', '-e', 'DROP DATABASE %s' % name)
assert ('database doesn\'t exist' in r.std_err
r = run_mysql('-e', 'DROP DATABASE %s' % name)
assert ('database doesn\'t exist' in force_text(r.std_err)
or r.status_code == 0)
return

Expand All @@ -61,7 +74,7 @@ def db_exists():
return r.status_code == 0

if get_db_engine() == 'mysql':
r = run_cmd('psql', TEST_DB_NAME, '-e', 'SELECT 1')
r = run_mysql(TEST_DB_NAME, '-e', 'SELECT 1')
return r.status_code == 0

raise AssertionError('%s cannot be tested properly!' % get_db_engine())
Expand All @@ -74,7 +87,7 @@ def mark_database():
return

if get_db_engine() == 'mysql':
r = run_cmd('mysql', TEST_DB_NAME, '-e', 'CREATE TABLE mark_table(kaka int);')
r = run_mysql(TEST_DB_NAME, '-e', 'CREATE TABLE mark_table(kaka int);')
assert r.status_code == 0
return

Expand All @@ -89,7 +102,7 @@ def mark_exists():
return bool(r.std_out)

if get_db_engine() == 'mysql':
r = run_cmd('mysql', TEST_DB_NAME, '-e', 'SELECT 1 FROM mark_table')
r = run_mysql(TEST_DB_NAME, '-e', 'SELECT 1 FROM mark_table')

return r.status_code == 0

Expand Down
2 changes: 1 addition & 1 deletion tests/settings_mysql.py
@@ -1,4 +1,4 @@
from settings_base import *
from tests.settings_base import *

DATABASES = {
'default': {
Expand Down
2 changes: 1 addition & 1 deletion tests/settings_postgres.py
@@ -1,4 +1,4 @@
from settings_base import *
from tests.settings_base import *

# PyPy compatibility
try:
Expand Down
2 changes: 1 addition & 1 deletion tests/settings_sqlite.py
@@ -1,4 +1,4 @@
from settings_base import *
from tests.settings_base import *

DATABASES = {
'default': {
Expand Down
2 changes: 1 addition & 1 deletion tests/test_db_setup.py
Expand Up @@ -19,7 +19,7 @@ def test_db_reuse(django_testdir):
create_test_module(django_testdir, '''
import pytest
from app.models import Item
from .app.models import Item
@pytest.mark.django_db
def test_db_can_be_accessed():
Expand Down
3 changes: 1 addition & 2 deletions tests/test_django_settings_module.py
Expand Up @@ -74,8 +74,7 @@ def test_ds_non_existent(testdir, monkeypatch):
monkeypatch.setenv('DJANGO_SETTINGS_MODULE', 'DOES_NOT_EXIST')
testdir.makepyfile('def test_ds(): pass')
result = testdir.runpytest()
result.stderr.fnmatch_lines(['''*Could not import settings 'DOES_NOT_EXIST' (Is it on sys.path?): No module named DOES_NOT_EXIST*'''])

result.stderr.fnmatch_lines(['''*Could not import settings 'DOES_NOT_EXIST' (Is it on sys.path?):*'''])

def test_django_settings_configure(testdir, monkeypatch):
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/test_environment.py
Expand Up @@ -3,7 +3,7 @@
import pytest
from django.core import mail
from django.db import connection
from app.models import Item
from .app.models import Item


# It doesn't matter which order all the _again methods are run, we just need
Expand Down
38 changes: 20 additions & 18 deletions tests/test_fixtures.py
Expand Up @@ -6,15 +6,15 @@

from __future__ import with_statement

import urllib

import py
import django
import pytest
from django.conf import settings as real_settings
from django.test.client import Client, RequestFactory

from .app.models import Item
from .test_database import noop_transactions
from .compat import force_text, urlopen


django # Avoid pyflakes complaints
Expand All @@ -27,12 +27,14 @@ def test_client(client):
@pytest.mark.django_db
def test_admin_client(admin_client):
assert isinstance(admin_client, Client)
assert admin_client.get('/admin-required/').content == 'You are an admin'
resp = admin_client.get('/admin-required/')
assert force_text(resp.content) == 'You are an admin'


def test_admin_client_no_db_marker(admin_client):
assert isinstance(admin_client, Client)
assert admin_client.get('/admin-required/').content == 'You are an admin'
resp = admin_client.get('/admin-required/')
assert force_text(resp.content) == 'You are an admin'


def test_rf(rf):
Expand Down Expand Up @@ -81,29 +83,29 @@ class TestLiveServer:
pytest.mark.skipif('django.VERSION[:2] < (1, 4)'),
pytest.mark.urls('tests.urls_liveserver'),
]

def test_url(self, live_server):
assert live_server.url == unicode(live_server)
assert live_server.url == force_text(live_server)

def test_transactions(self, live_server):
assert not noop_transactions()

def test_db_changes_visibility(self, live_server):
response_data = urllib.urlopen(live_server + '/item_count/').read()
assert response_data == 'Item count: 0'
response_data = urlopen(live_server + '/item_count/').read()
assert force_text(response_data) == 'Item count: 0'
Item.objects.create(name='foo')
response_data = urllib.urlopen(live_server + '/item_count/').read()
assert response_data == 'Item count: 1'
response_data = urlopen(live_server + '/item_count/').read()
assert force_text(response_data) == 'Item count: 1'

def test_fixture_db(self, db, live_server):
Item.objects.create(name='foo')
response_data = urllib.urlopen(live_server + '/item_count/').read()
assert response_data == 'Item count: 1'
response_data = urlopen(live_server + '/item_count/').read()
assert force_text(response_data) == 'Item count: 1'

def test_fixture_transactional_db(self, transactional_db, live_server):
Item.objects.create(name='foo')
response_data = urllib.urlopen(live_server + '/item_count/').read()
assert response_data == 'Item count: 1'
response_data = urlopen(live_server + '/item_count/').read()
assert force_text(response_data) == 'Item count: 1'

@pytest.fixture
def item(self):
Expand All @@ -123,13 +125,13 @@ def item_db(self, db):
return Item.objects.create(name='foo')

def test_item_db(self, item_db, live_server):
response_data = urllib.urlopen(live_server + '/item_count/').read()
assert response_data == 'Item count: 1'
response_data = urlopen(live_server + '/item_count/').read()
assert force_text(response_data) == 'Item count: 1'

@pytest.fixture
def item_transactional_db(self, transactional_db):
return Item.objects.create(name='foo')

def test_item_transactional_db(self, item_transactional_db, live_server):
response_data = urllib.urlopen(live_server + '/item_count/').read()
assert response_data == 'Item count: 1'
response_data = urlopen(live_server + '/item_count/').read()
assert force_text(response_data) == 'Item count: 1'

0 comments on commit 0723bc6

Please sign in to comment.