Compare commits

..

8 Commits

Author SHA1 Message Date
Stefan Wojcik
1178ae262b Truly test pymongo v2.8 instead of v2.9.
Testing v2.9 was unexpected and it's not as important given v2.9 mostly
mostly just provided an upgrade path for v3.0, and we already test v3.x.
2017-02-25 16:18:20 -05:00
Stefan Wojcik
4c629e46ce Drop pymongo dev tests to speed things up.
We don't need to test it before a stable release and removing it shaves off
considerable amount of time from Travis builds.
2017-02-25 16:06:19 -05:00
Stefan Wojcik
2d429e3703 use mongodb v2.6 bcos pymongo 2.7 doesnt work w/ mongodb v3.0 2017-02-25 15:49:28 -05:00
Stefan Wojcik
06cb866f82 tox.ini cleanup 2017-02-25 14:07:58 -05:00
Stefan Wojcik
e9caaa916c try freezing mongodb at the latest v3.0 2017-02-19 18:20:41 -05:00
Stefan Wojcik
4e91c4f5bc use apt addons vs sudo apt-get install 2017-02-19 18:02:30 -05:00
Stefan Wojcik
4068a6b36b cache pip dependencies 2017-02-19 17:59:37 -05:00
Stefan Wojcik
d096d2ec38 run Travis builds in a container-based environment 2017-02-19 17:49:05 -05:00
9 changed files with 105 additions and 123 deletions

View File

@@ -1,3 +1,6 @@
# Use a container-based environment
sudo: false
language: python language: python
python: python:
@@ -8,26 +11,41 @@ python:
- pypy - pypy
- pypy3 - pypy3
# Test on PyMongo v2.7.x, v2.8.x, and v3.x
env: env:
- PYMONGO=2.7 - PYMONGO=2.7
- PYMONGO=2.8 - PYMONGO=2.8
- PYMONGO=3.0 - PYMONGO=3.0
- PYMONGO=dev
matrix: matrix:
fast_finish: true fast_finish: true
before_install: services:
- travis_retry sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 - mongodb
- echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' |
sudo tee /etc/apt/sources.list.d/mongodb.list addons:
- travis_retry sudo apt-get update apt:
- travis_retry sudo apt-get install mongodb-org-server sources:
- mongodb-upstart
packages:
- mongodb-org-server=2.6.9
# Optional dependencies for the ImageField and others
- python-dev
- python3-dev
- libopenjpeg-dev
- zlib1g-dev
- libjpeg-turbo8-dev
- libtiff4-dev
- libjpeg8-dev
- libfreetype6-dev
- liblcms2-dev
- libwebp-dev
- tcl8.5-dev
- tk8.5-dev
- python-tk
install: install:
- sudo apt-get install python-dev python3-dev libopenjpeg-dev zlib1g-dev libjpeg-turbo8-dev
libtiff4-dev libjpeg8-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev
python-tk
- travis_retry pip install --upgrade pip - travis_retry pip install --upgrade pip
- travis_retry pip install coveralls - travis_retry pip install coveralls
- travis_retry pip install flake8 - travis_retry pip install flake8
@@ -35,6 +53,9 @@ install:
- travis_retry pip install "virtualenv<14.0.0" # virtualenv>=14.0.0 has dropped Python 3.2 support (and pypy3 is based on py32) - travis_retry pip install "virtualenv<14.0.0" # virtualenv>=14.0.0 has dropped Python 3.2 support (and pypy3 is based on py32)
- travis_retry tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -e test - travis_retry tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -e test
# Cache dependencies installed via pip
cache: pip
# Run flake8 for py27 # Run flake8 for py27
before_script: before_script:
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then tox -e flake8; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then tox -e flake8; fi
@@ -45,7 +66,7 @@ script:
# For now only submit coveralls for Python v2.7. Python v3.x currently shows # For now only submit coveralls for Python v2.7. Python v3.x currently shows
# 0% coverage. That's caused by 'use_2to3', which builds the py3-compatible # 0% coverage. That's caused by 'use_2to3', which builds the py3-compatible
# code in a separate dir and runs tests on that. # code in a separate dir and runs tests on that.
after_script: after_success:
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coveralls --verbose; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coveralls --verbose; fi
notifications: notifications:
@@ -56,11 +77,22 @@ branches:
- master - master
- /^v.*$/ - /^v.*$/
# Whenever a new release is created via GitHub, publish it on PyPI.
deploy: deploy:
provider: pypi provider: pypi
user: the_drow user: the_drow
password: password:
secure: QMyatmWBnC6ZN3XLW2+fTBDU4LQcp1m/LjR2/0uamyeUzWKdlOoh/Wx5elOgLwt/8N9ppdPeG83ose1jOz69l5G0MUMjv8n/RIcMFSpCT59tGYqn3kh55b0cIZXFT9ar+5cxlif6a5rS72IHm5li7QQyxexJIII6Uxp0kpvUmek= secure: QMyatmWBnC6ZN3XLW2+fTBDU4LQcp1m/LjR2/0uamyeUzWKdlOoh/Wx5elOgLwt/8N9ppdPeG83ose1jOz69l5G0MUMjv8n/RIcMFSpCT59tGYqn3kh55b0cIZXFT9ar+5cxlif6a5rS72IHm5li7QQyxexJIII6Uxp0kpvUmek=
# create a source distribution and a pure python wheel for faster installs
distributions: "sdist bdist_wheel"
# only deploy on tagged commits (aka GitHub releases) and only for the
# parent repo's builds running Python 2.7 along with dev PyMongo (we run
# Travis against many different Python and PyMongo versions and we don't
# want the deploy to occur multiple times).
on: on:
tags: true tags: true
repo: MongoEngine/mongoengine repo: MongoEngine/mongoengine
condition: "$PYMONGO = 3.0"
python: 2.7

View File

@@ -4,8 +4,7 @@ Changelog
Development Development
=========== ===========
- (Fill this out as you fix issues and develop your features). - (Fill this out as you fix issues and develop you features).
- Fixed using sets in field choices #1481
- POTENTIAL BREAKING CHANGE: Fixed limit/skip/hint/batch_size chaining #1476 - POTENTIAL BREAKING CHANGE: Fixed limit/skip/hint/batch_size chaining #1476
- POTENTIAL BREAKING CHANGE: Changed a public `QuerySet.clone_into` method to a private `QuerySet._clone_into` #1476 - POTENTIAL BREAKING CHANGE: Changed a public `QuerySet.clone_into` method to a private `QuerySet._clone_into` #1476
- Fixed connecting to a replica set with PyMongo 2.x #1436 - Fixed connecting to a replica set with PyMongo 2.x #1436

View File

@@ -150,7 +150,7 @@ arguments can be set on all fields:
.. note:: If set, this field is also accessible through the `pk` field. .. note:: If set, this field is also accessible through the `pk` field.
:attr:`choices` (Default: None) :attr:`choices` (Default: None)
An iterable (e.g. list, tuple or set) of choices to which the value of this An iterable (e.g. a list or tuple) of choices to which the value of this
field should be limited. field should be limited.
Can be either be a nested tuples of value (stored in mongo) and a Can be either be a nested tuples of value (stored in mongo) and a

View File

@@ -193,8 +193,7 @@ class BaseField(object):
EmbeddedDocument = _import_class('EmbeddedDocument') EmbeddedDocument = _import_class('EmbeddedDocument')
choice_list = self.choices choice_list = self.choices
if isinstance(next(iter(choice_list)), (list, tuple)): if isinstance(choice_list[0], (list, tuple)):
# next(iter) is useful for sets
choice_list = [k for k, _ in choice_list] choice_list = [k for k, _ in choice_list]
# Choices which are other types of Documents # Choices which are other types of Documents

View File

@@ -139,12 +139,12 @@ class URLField(StringField):
# Check first if the scheme is valid # Check first if the scheme is valid
scheme = value.split('://')[0].lower() scheme = value.split('://')[0].lower()
if scheme not in self.schemes: if scheme not in self.schemes:
self.error(u'Invalid scheme {} in URL: {}'.format(scheme, value)) self.error('Invalid scheme {} in URL: {}'.format(scheme, value))
return return
# Then check full URL # Then check full URL
if not self.url_regex.match(value): if not self.url_regex.match(value):
self.error(u'Invalid URL: {}'.format(value)) self.error('Invalid URL: {}'.format(value))
return return

View File

@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import six
from nose.plugins.skip import SkipTest
import datetime import datetime
import unittest import unittest
import uuid import uuid
import math import math
import itertools import itertools
import re import re
from nose.plugins.skip import SkipTest
import six import six
try: try:
@@ -26,13 +27,21 @@ from mongoengine import *
from mongoengine.connection import get_db from mongoengine.connection import get_db
from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList, from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList,
_document_registry) _document_registry)
from mongoengine.errors import NotRegistered, DoesNotExist
from tests.utils import MongoDBTestCase
__all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase") __all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase")
class FieldTest(MongoDBTestCase): class FieldTest(unittest.TestCase):
def setUp(self):
connect(db='mongoenginetest')
self.db = get_db()
def tearDown(self):
self.db.drop_collection('fs.files')
self.db.drop_collection('fs.chunks')
self.db.drop_collection('mongoengine.counters')
def test_default_values_nothing_set(self): def test_default_values_nothing_set(self):
"""Ensure that default field values are used when creating a document. """Ensure that default field values are used when creating a document.
@@ -218,9 +227,9 @@ class FieldTest(MongoDBTestCase):
self.assertTrue(isinstance(ret.comp_dt_fld, datetime.datetime)) self.assertTrue(isinstance(ret.comp_dt_fld, datetime.datetime))
def test_not_required_handles_none_from_database(self): def test_not_required_handles_none_from_database(self):
"""Ensure that every field can handle null values from the """Ensure that every fields can handle null values from the database.
database.
""" """
class HandleNoneFields(Document): class HandleNoneFields(Document):
str_fld = StringField(required=True) str_fld = StringField(required=True)
int_fld = IntField(required=True) int_fld = IntField(required=True)
@@ -341,12 +350,11 @@ class FieldTest(MongoDBTestCase):
person.validate() person.validate()
def test_url_validation(self): def test_url_validation(self):
"""Ensure that URLFields validate urls properly.""" """Ensure that URLFields validate urls properly.
"""
class Link(Document): class Link(Document):
url = URLField() url = URLField()
Link.drop_collection()
link = Link() link = Link()
link.url = 'google' link.url = 'google'
self.assertRaises(ValidationError, link.validate) self.assertRaises(ValidationError, link.validate)
@@ -354,27 +362,6 @@ class FieldTest(MongoDBTestCase):
link.url = 'http://www.google.com:8080' link.url = 'http://www.google.com:8080'
link.validate() link.validate()
def test_unicode_url_validation(self):
"""Ensure unicode URLs are validated properly."""
class Link(Document):
url = URLField()
Link.drop_collection()
link = Link()
link.url = u'http://привет.com'
# TODO fix URL validation - this *IS* a valid URL
# For now we just want to make sure that the error message is correct
try:
link.validate()
self.assertTrue(False)
except ValidationError as e:
self.assertEqual(
unicode(e),
u"ValidationError (Link:None) (Invalid URL: http://\u043f\u0440\u0438\u0432\u0435\u0442.com: ['url'])"
)
def test_url_scheme_validation(self): def test_url_scheme_validation(self):
"""Ensure that URLFields validate urls with specific schemes properly. """Ensure that URLFields validate urls with specific schemes properly.
""" """
@@ -3199,42 +3186,26 @@ class FieldTest(MongoDBTestCase):
att.delete() att.delete()
self.assertEqual(0, Attachment.objects.count()) self.assertEqual(0, Attachment.objects.count())
def test_choices_allow_using_sets_as_choices(self): def test_choices_validation(self):
"""Ensure that sets can be used when setting choices """Ensure that value is in a container of allowed values.
""" """
class Shirt(Document): class Shirt(Document):
size = StringField(choices={'M', 'L'}) size = StringField(max_length=3, choices=(
('S', 'Small'), ('M', 'Medium'), ('L', 'Large'),
('XL', 'Extra Large'), ('XXL', 'Extra Extra Large')))
Shirt(size='M').validate() Shirt.drop_collection()
def test_choices_validation_allow_no_value(self):
"""Ensure that .validate passes and no value was provided
for a field setup with choices
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
shirt = Shirt() shirt = Shirt()
shirt.validate() shirt.validate()
def test_choices_validation_accept_possible_value(self): shirt.size = "S"
"""Ensure that value is in a container of allowed values.
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
shirt = Shirt(size='S')
shirt.validate() shirt.validate()
def test_choices_validation_reject_unknown_value(self): shirt.size = "XS"
"""Ensure that unallowed value are rejected upon validation self.assertRaises(ValidationError, shirt.validate)
"""
class Shirt(Document):
size = StringField(choices=('S', 'M'))
shirt = Shirt(size="XS") Shirt.drop_collection()
with self.assertRaises(ValidationError):
shirt.validate()
def test_choices_validation_documents(self): def test_choices_validation_documents(self):
""" """
@@ -4053,13 +4024,12 @@ class FieldTest(MongoDBTestCase):
self.assertTrue(isinstance(doc.some_long, six.integer_types)) self.assertTrue(isinstance(doc.some_long, six.integer_types))
class EmbeddedDocumentListFieldTestCase(MongoDBTestCase): class EmbeddedDocumentListFieldTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.db = connect(db='EmbeddedDocumentListFieldTestCase')
def setUp(self):
"""
Create two BlogPost entries in the database, each with
several EmbeddedDocuments.
"""
class Comments(EmbeddedDocument): class Comments(EmbeddedDocument):
author = StringField() author = StringField()
message = StringField() message = StringField()
@@ -4067,11 +4037,14 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
class BlogPost(Document): class BlogPost(Document):
comments = EmbeddedDocumentListField(Comments) comments = EmbeddedDocumentListField(Comments)
BlogPost.drop_collection() cls.Comments = Comments
cls.BlogPost = BlogPost
self.Comments = Comments
self.BlogPost = BlogPost
def setUp(self):
"""
Create two BlogPost entries in the database, each with
several EmbeddedDocuments.
"""
self.post1 = self.BlogPost(comments=[ self.post1 = self.BlogPost(comments=[
self.Comments(author='user1', message='message1'), self.Comments(author='user1', message='message1'),
self.Comments(author='user2', message='message1') self.Comments(author='user2', message='message1')
@@ -4083,6 +4056,13 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
self.Comments(author='user3', message='message1') self.Comments(author='user3', message='message1')
]).save() ]).save()
def tearDown(self):
self.BlogPost.drop_collection()
@classmethod
def tearDownClass(cls):
cls.db.drop_database('EmbeddedDocumentListFieldTestCase')
def test_no_keyword_filter(self): def test_no_keyword_filter(self):
""" """
Tests the filter method of a List of Embedded Documents Tests the filter method of a List of Embedded Documents
@@ -4440,8 +4420,7 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
my_list = ListField(EmbeddedDocumentField(EmbeddedWithUnique)) my_list = ListField(EmbeddedDocumentField(EmbeddedWithUnique))
A(my_list=[]).save() A(my_list=[]).save()
with self.assertRaises(NotUniqueError): self.assertRaises(NotUniqueError, lambda: A(my_list=[]).save())
A(my_list=[]).save()
class EmbeddedWithSparseUnique(EmbeddedDocument): class EmbeddedWithSparseUnique(EmbeddedDocument):
number = IntField(unique=True, sparse=True) number = IntField(unique=True, sparse=True)
@@ -4449,9 +4428,6 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
class B(Document): class B(Document):
my_list = ListField(EmbeddedDocumentField(EmbeddedWithSparseUnique)) my_list = ListField(EmbeddedDocumentField(EmbeddedWithSparseUnique))
A.drop_collection()
B.drop_collection()
B(my_list=[]).save() B(my_list=[]).save()
B(my_list=[]).save() B(my_list=[]).save()
@@ -4491,8 +4467,6 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
a_field = IntField() a_field = IntField()
c_field = IntField(custom_data=custom_data) c_field = IntField(custom_data=custom_data)
CustomData.drop_collection()
a1 = CustomData(a_field=1, c_field=2).save() a1 = CustomData(a_field=1, c_field=2).save()
self.assertEqual(2, a1.c_field) self.assertEqual(2, a1.c_field)
self.assertFalse(hasattr(a1.c_field, 'custom_data')) self.assertFalse(hasattr(a1.c_field, 'custom_data'))

View File

@@ -18,13 +18,15 @@ try:
except ImportError: except ImportError:
HAS_PIL = False HAS_PIL = False
from tests.utils import MongoDBTestCase
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png') TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png')
TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), 'mongodb_leaf.png') TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), 'mongodb_leaf.png')
class FileTest(MongoDBTestCase): class FileTest(unittest.TestCase):
def setUp(self):
connect(db='mongoenginetest')
self.db = get_db()
def tearDown(self): def tearDown(self):
self.db.drop_collection('fs.files') self.db.drop_collection('fs.files')

View File

@@ -1,22 +0,0 @@
import unittest
from mongoengine import connect
from mongoengine.connection import get_db
MONGO_TEST_DB = 'mongoenginetest'
class MongoDBTestCase(unittest.TestCase):
"""Base class for tests that need a mongodb connection
db is being dropped automatically
"""
@classmethod
def setUpClass(cls):
cls._connection = connect(db=MONGO_TEST_DB)
cls._connection.drop_database(MONGO_TEST_DB)
cls.db = get_db()
@classmethod
def tearDownClass(cls):
cls._connection.drop_database(MONGO_TEST_DB)

View File

@@ -1,5 +1,5 @@
[tox] [tox]
envlist = {py26,py27,py33,py34,py35,pypy,pypy3}-{mg27,mg28},flake8 envlist = {py27,py33,py34,py35,pypy,pypy3}-{mg27,mg28,mg30},flake8
[testenv] [testenv]
commands = commands =
@@ -7,12 +7,10 @@ commands =
deps = deps =
nose nose
mg27: PyMongo<2.8 mg27: PyMongo<2.8
mg28: PyMongo>=2.8,<3.0 mg28: PyMongo>=2.8,<2.9
mg30: PyMongo>=3.0 mg30: PyMongo>=3.0
mgdev: https://github.com/mongodb/mongo-python-driver/tarball/master
setenv = setenv =
PYTHON_EGG_CACHE = {envdir}/python-eggs PYTHON_EGG_CACHE = {envdir}/python-eggs
passenv = windir
[testenv:flake8] [testenv:flake8]
deps = deps =