I've been successfully using pytest for quite a while now to do unit 
testing for my web2py apps. Today, after upgrading to the latest web2py 
version (Version 2.10.4-stable+timestamp.2015.04.26.15.11.54) my tests 
won't run. When I try to run the tests in a test file (in the tests/ 
directory) web2py tries to import that test file as a module and raises an 
ImportError.

For example, I have a test file at 
myapp/tests/modules/test_greek_parser.py. I try to run it like this:

python2.7 -m pytest -xvs applications/paideia/tests/modules/
test_greek_parser.py

The error looks like this:

================================================= test session starts 
=====================================
platform linux2 -- Python 2.7.9 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python 
                                      
collected 0 items / 1 errors                                               
                                           
                                                                            
                                          
======================================================= ERRORS 
============================================
______________________ ERROR collecting applications/paideia/tests/modules/
test_greek_parser.py ___________
/usr/local/lib/python2.7/dist-packages/py/_path/local.py:620: in pyimport   
                                           
>           __import__(modname)                                             
                                           
gluon/custom_import.py:89: in custom_importer                               
                                           
>                               raise ImportError, 'Cannot import module %s' 
% str(e)                               
E                               ImportError: Cannot import module 
'applications.paideia.modules.test_greek_parser'    
=============================================== 1 error in 0.01 seconds 
===================================


It's not surprising that custom_importer can't find the module, since it 
doesn't exist! It's looking for a file with my test file name, but looking 
in the myapp/modules/ directory. I'm not even sure why gluon is trying to 
import something with that file name at all.

Now, if this isn't strange enough already, this import error only happens 
with some of my test files. Others (in the same tests/modules/ directory) 
run without a hitch. But I can't for the life of me find any significant 
difference between the two.

I'm attaching a couple of files here in case they help shed any light:

1. my conftest.py which is read by pytest before running the tests (this 
seems to be fine; it sits in the parent 'tests' directory)
2. test_paideia_utils.py (which runs without any problem)
3. test_greek_parser.py (which raises the import error)

I realize that this may appear to be a pytest problem rather than a web2py 
problem. But the error is happening because custom_importer is being called 
somewhere, so at the least it looks like some recent update has introduced 
an incompatibility with pytest. Again, these tests were all running fine 
(with the same conftest.py)

Thanks again for any help. I'm really at a loss with this one!

-- 
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
--- 
You received this message because you are subscribed to the Google Groups 
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to web2py+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
#!/usr/bin/env python

'''
py.test configuration and fixtures file.

This file is lightly adapted from the one by viniciusban;
Part of the web2py.test model app (https://github.com/viniciusban/web2py.test)

This file
- Tells application it's running in a test environment.
- Creates a complete web2py environment, similar to web2py shell.
- Creates a WebClient instance to browse your application, similar to a real
web browser.
- Propagates some application data to test cases via fixtures, like baseurl
and automatic appname discovery.

To write to db in test:

web2py.db.table.insert(**data)
web2py.db.commit()

To run tests:

cd web2py (you must be in web2py root directory to run tests)
python web2py.py -a my_password --nogui &
py.test -x [-l] [-q|-v] -s applications/my_app_name/tests

'''

import os
import pytest
import sys
from pprint import pprint
sys.path.insert(0, '')

# allow imports from modules and site-packages
dirs = os.path.split(__file__)[0]
appname = dirs.split(os.path.sep)[-2]
modules_path = os.path.join('applications', appname, 'modules')
if modules_path not in sys.path:
    sys.path.append(modules_path)  # imports from app modules folder
if 'site-packages' not in sys.path:
    sys.path.append('site-packages')  # imports from site-packages

from gluon.shell import env
web2py_env = env(appname, import_models=True,
                 extra_request=dict(is_local=True))


@pytest.fixture(scope='module')
def baseurl(appname):
    '''The base url to call your application.

    Change you port number as necessary.
    '''

    return 'http://localhost:8000/%s' % appname


@pytest.fixture(scope='module')
def appname():
    '''Discover application name.

    Your test scripts must be on applications/<your_app>/tests
    '''

    dirs = os.path.split(__file__)[0]
    appname = dirs.split(os.path.sep)[-2]
    return appname


@pytest.fixture(scope='module', autouse=True)
def fixture_create_testfile_for_application(request, appname):
    '''
    Creates a temp file to tell application she's running under a
    test environment.

    Usually you will want to create your database in memory to speed up
    your tests and not change your development database.

    This fixture is automatically run by py.test at module level. So, there's
    no overhad to test performance.
    '''
    import os
    import shutil

    # note: if you use Ubuntu, you can allocate your test database on ramdisk
    # simply using the /dev/shm directory.
    temp_dir = '/dev/shm/' + appname

    if not os.path.isdir(temp_dir):
        os.mkdir(temp_dir)

    # IMPORTANT: temp_filename must be the same as set in app/models/db.py
    temp_filename = '%s/tests_%s.tmp' % (temp_dir, appname)

    with open(temp_filename, 'w+') as tempfile:
        tempfile.write('application %s running in test mode' % appname)

    def _remove_temp_file_after_tests():
        shutil.rmtree(temp_dir)
    request.addfinalizer(_remove_temp_file_after_tests)


@pytest.fixture(autouse=True)
def fixture_cleanup_db(web2py):
    '''Truncate all database tables before every single test case.

    This can really slow down your tests. So, keep your test data small and try
    to allocate your database in memory.

    Automatically called by test.py due to decorator.
    '''

    # TODO: figure out db rollback to standard state (not truncate to None)
    # web2py.db.rollback()
    # for tab in web2py.db.tables:
    #     web2py.db[tab].truncate()
    # web2py.db.commit()
    print('ran fixture cleanup !!!!!!!!!!!!!!!!!!!!!!!!!')
    pass


@pytest.fixture(scope='module')
def client(baseurl, fixture_create_testfile_for_application):
    '''Create a new WebClient instance once per module.
    '''
    from gluon.contrib.webclient import WebClient
    webclient = WebClient(baseurl)
    return webclient


@pytest.fixture(scope='module')
def user_login(request, web2py, client, db):
    """
    Provide a new, registered, and logged-in user account for testing.
    """
    # register test user
    auth = web2py.current.auth
    reg_data = {'first_name': 'Homer',
                'last_name': 'Simpson',
                'email': 'scotti...@gmail.com',
                'password': 'testing',
                'time_zone': 'America/Toronto'}
    # create new test user if necessary and delete if there's more than one
    user_query = db((db.auth_user.first_name == reg_data['first_name']) &
                    (db.auth_user.last_name == reg_data['last_name']) &
                    (db.auth_user.email == reg_data['email']) &
                    (db.auth_user.time_zone == reg_data['time_zone']))
    user_count = user_query.count()
    if user_count == 0:
        auth.table_user().insert(**reg_data)
    elif user_count > 1:
        u_count = user_count
        userset = user_query.select()
        while u_count > 1:
            lastu = userset.last()
            lastu.delete_record()
            u_count -= 1
    else:
        pass

    assert user_query.count() == 1
    user_record = user_query.select().first()
    assert user_record

    # log test user in
    auth.login_user(user_record)
    assert auth.is_logged_in()

    def fin():
        """
        Delete the test user's account.
        """
        user_record.delete_record()
        # TODO: remove test user's performance record
        assert user_query.count() == 0

    request.addfinalizer(fin)
    return user_record.as_dict()


@pytest.fixture(scope='module')
def web2py(appname, fixture_create_testfile_for_application):
    '''
    Create a Web2py environment similar to that achieved by Web2py shell.

    It allows you to use global Web2py objects like db, request, response,
    session, etc.

    Concerning tests, it is usually used to check if your database is an
    expected state, avoiding creating controllers and functions to help
    tests.
    '''

    from gluon.shell import env
    from gluon.storage import Storage

    web2py_env = env(appname, import_models=True,
                     extra_request=dict(is_local=True))

    # Uncomment next 3 lines to allow using global Web2py objects directly
    # in your test scripts.
    if hasattr(web2py_env, '__file__'):
        del web2py_env['__file__']  # avoid py.test import error
    globals().update(web2py_env)

    return Storage(web2py_env)


@pytest.fixture(scope='module')
def db(web2py, request):
    """
    Provides a access to the production database from within test functions.

    While the web2py object (providing global framework objects standard in a
    web2py session) has a module-level scope, this db fixture has a function-
    level scope. This means that any code below is run for each test function,
    allowing for function-level db setup and teardown.

    The fin teardown function expects a 'newrows' dictionary to be created in
    the requesting test function. This dictionary should have db table names as
    its keys and a list of newly-inserted row id numbers as the values.
    """
    mydb = web2py.db
    newrows = getattr(request.node.instance, 'newrows', None)

    def fin():
        """
        Delete any newly inserted rows in the test database.
        """
        print('checking for inserted rows to remove**********')
        pprint(newrows)
        if newrows:
            print('removing')
            for tbl, rowids in newrows.iteritems():
                mydb(mydb[tbl].id.belongs(rowids)).delete()
                for i in rowids:
                    assert not mydb[mydb[tbl]](i)

    request.addfinalizer(fin)
    return mydb
#! /usr/bin/python
# -*- coding: UTF-8 -*-
"""
 Unit tests for the paideia_utils module

 Configuration and some fixtures
 the file tests/conftest.py
 run with py.test -xvs path/to/tests/dir

"""

import pytest
from paideia_utils import GreekNormalizer, check_regex
from pprint import pprint


class TestGreekNormalizer():
    """
    """
    @pytest.mark.skipif(False, reason='just because')
    @pytest.mark.parametrize('string_in,string_out',
                                [('ἄγαπὴ', u'ἀγαπη'),  # handle multiple accents
                                 ('“ἀγαπη”', u'"ἀγαπη"'),  # handle curly quotes
                                 ('‘ἀγαπη’', u"'ἀγαπη'"),
                                 (u'ἀγάπη', u'ἀγαπη'),  # handle unicode input
                                 ('τίνος', u'τίνος'),  # words to be *kept* accented
                                 ('τί ἐστῖν', u'τί ἐστιν'),
                                 ('τίς', u'τίς'),
                                 ('τίνα', u'τίνα'),
                                 ('τίνας', u'τίνας'),
                                 ('τίνι', u'τίνι'),
                                 ('Τίνος', u'Τίνος'),
                                 ('Τί', u'Τί'),
                                 ('Τίς', u'Τίς'),
                                 ('Τίνα', u'Τίνα'),
                                 ('Τίνας', u'Τίνας'),
                                 ('Τίνι', u'Τίνι'),
                                 ('τίς', u'τίς'),  # handle q-iota on windows
                                 ('ἀγάπη', u'ἀγαπη'),  # alpha
                                 ('ἀγὰπη', u'ἀγαπη'),
                                 ('ἀγᾶπη', u'ἀγαπη'),
                                 ('ἄν', u'ἀν'),  # alpha with smooth breathing
                                 ('ἂν', u'ἀν'),
                                 ('ἆν', u'ἀν'),
                                 ('ἅν', u'ἁν'),  # alpha with rough breathing
                                 ('ἃν', u'ἁν'),
                                 ('ἇν', u'ἁν'),
                                 ('πᾷν', u'πᾳν'),  # alpha with iota subscript
                                 ('πᾲν', u'πᾳν'),
                                 ('πᾴν', u'πᾳν'),
                                 ('ᾄν', u'ᾀν'),  # alpha with iota subscript & smooth
                                 ('ᾂν', u'ᾀν'),
                                 ('ᾆν', u'ᾀν'),
                                 ('ᾅν', u'ᾁν'),  # alpha with iota subscript & rough
                                 ('ᾃν', u'ᾁν'),
                                 ('ᾇν', u'ᾁν'),
                                 ('πέν', u'πεν'),  # epsilon
                                 ('πὲν', u'πεν'),
                                 ('ἒν', u'ἐν'),  # epsilon with smooth
                                 ('ἔν', u'ἐν'),
                                 ('ἕν', u'ἑν'),  # epsilon with rough
                                 ('ἓν', u'ἑν'),
                                 ('πῆν', u'πην'),  # eta
                                 ('πήν', u'πην'),
                                 ('πὴν', u'πην'),
                                 ('ἤν', u'ἠν'),  # eta with smooth
                                 ('ἢν', u'ἠν'),
                                 ('ἦν', u'ἠν'),
                                 ('ἥν', u'ἡν'),  # eta with rough
                                 ('ἣν', u'ἡν'),
                                 ('ἧν', u'ἡν'),
                                 ('πῇν', u'πῃν'),  # eta with iota subscript
                                 ('πῄν', u'πῃν'),
                                 ('πῂν', u'πῃν'),
                                 ("ᾕν", u"ᾑν"),  # eta with subsc and rough
                                 ("ᾓν", u"ᾑν"),
                                 ("ᾗν", u"ᾑν"),
                                 ("ᾔν", u"ᾐν"),  # eta with subsc and smooth
                                 ("ᾒν", u"ᾐν"),
                                 ("ᾖν", u"ᾐν"),
                                 ("όν", u"ον"),  # omicron
                                 ("ὸν", u"ον"),
                                 ("ὅν", u"ὁν"),  # omicron with rough
                                 ("ὃν", u"ὁν"),
                                 ("ὄν", u"ὀν"),  # omicron with smooth
                                 ("ὂν", u"ὀν"),
                                 ("ῖν", u"ιν"),  # iota
                                 ("ϊν", u"ιν"),
                                 ("ίν", u"ιν"),
                                 ("ὶν", u"ιν"),
                                 ("ίν", u"ιν"),
                                 ("ἵν", u"ἱν"),  # iota with rough
                                 ("ἳν", u"ἱν"),
                                 ("ἷν", u"ἱν"),
                                 ("ἴν", u"ἰν"),  # iota with smooth
                                 ("ἲν", u"ἰν"),
                                 ("ἶν", u"ἰν"),
                                 ("ῦν", u"υν"),  # upsilon
                                 ("ϋν", u"υν"),
                                 ("ύν", u"υν"),
                                 ("ὺν", u"υν"),
                                 ("ὕν", u"ὑν"),  # upsilon with rough
                                 ("ὓν", u"ὑν"),
                                 ("ὗν", u"ὑν"),
                                 ("ὔν", u"ὐν"),  # upsilon with smooth
                                 ("ὒν", u"ὐν"),
                                 ("ὖν", u"ὐν"),
                                 ("ῶν", u"ων"),  # omega
                                 ("ών", u"ων"),
                                 ("ὼν", u"ων"),
                                 ("ὥν", u"ὡν"),  # omega with rough
                                 ("ὣν", u"ὡν"),
                                 ("ὧν", u"ὡν"),
                                 ("ὤν", u"ὠν"),  # omega with smooth
                                 ("ὢν", u"ὠν"),
                                 ("ὦν", u"ὠν"),
                                 ("ῷν", u"ῳν"),  # omega with subsc
                                 ("ῴν", u"ῳν"),
                                 ("ῲν", u"ῳν"),
                                 ("ᾥν", u"ᾡν"),  # omega with subsc and rough
                                 ("ᾣν", u"ᾡν"),
                                 ("ᾧν", u"ᾡν"),
                                 ("ᾤν", u"ᾠν"),  # omega with subsc and smooth
                                 ("ᾢν", u"ᾠν"),
                                 ("ᾦν", u"ᾠν"),
                                 ("῾Ων", u"Ὡν"),  # improperly formed rough with caps
                                 ("῾Ρν", u"Ῥν"),
                                 ("῾Υν", u"Ὑν"),
                                 ("῾Αν", u"Ἁν"),
                                 ("῾Ον", u"Ὁν"),
                                 ("῾Εν", u"Ἑν"),
                                 ("῾Ιν", u"Ἱν"),
                                 ("᾿Αν", u"Ἀν"),  # improperly formed smooth with caps
                                 ("᾿Ον", u"Ὀν"),
                                 ("᾿Ων", u"Ὠν"),
                                 ("᾿Ιν", u"Ἰν"),
                                 ("᾿Εν", u"Ἐν"),
                                 ('Άπη', u'Απη'),  # alpha caps
                                 ('Ὰπη', u'Απη'),
                                 ('Ἄπη', u'Ἀπη'),
                                 ('Ἂπη', u'Ἀπη'),
                                 ('Ἅπη', u'Ἁπη'),
                                 ('Ἃπη', u'Ἁπη'),
                                 ("Έν", u"Εν"),  # epsilon caps
                                 ("Ὲν", u"Εν"),
                                 ("Ἕν", u"Ἑν"),
                                 ("Ἓν", u"Ἑν"),
                                 ("Ἔν", u"Ἐν"),
                                 ("Ἒν", u"Ἐν"),
                                 ("Ἥν", u"Ἡν"),  # eta caps
                                 ("Ἣν", u"Ἡν"),
                                 ("Ἧν", u"Ἡν"),
                                 ("Ἤν", u"Ἠν"),
                                 ("Ἢν", u"Ἠν"),
                                 ("Ἦν", u"Ἠν"),
                                 ("Ήν", u"Ην"),
                                 ("Ὴν", u"Ην"),
                                 ("Ἵν", u"Ἱν"),  # iota caps
                                 ("Ἳν", u"Ἱν"),
                                 ("Ἷν", u"Ἱν"),
                                 ("Ϊν", u"Ιν"),
                                 ("Ίν", u"Ιν"),
                                 ("Ὶν", u"Ιν"),
                                 ("Ίν", u"Ιν"),
                                 ("Ὅν", u"Ὁν"),  # omicron caps
                                 ("Ὃν", u"Ὁν"),
                                 ("Όν", u"Ον"),
                                 ("Ὸν", u"Ον"),
                                 ("Ὕν", u"Ὑν"),  # upsilon caps
                                 ("Ὓν", u"Ὑν"),
                                 ("Ὗν", u"Ὑν"),
                                 ("Ϋν", u"Υν"),
                                 ("Ύν", u"Υν"),
                                 ("Ὺν", u"Υν"),
                                 ("Ών", u"Ων"),  # omega caps
                                 ("Ὼν", u"Ων"),
                                 ("Ὥν", u"Ὡν"),
                                 ("Ὣν", u"Ὡν"),
                                 ("Ὧν", u"Ὡν"),
                                 ])
    def test_normalize_accents(self, string_in, string_out):
        """
        Unit testing function for paideia_utils.normalize_accents()

        """
        print 'string in', string_in
        print 'expected string out', string_out
        actual = GreekNormalizer().normalize_accents(string_in)
        print 'actual string out', actual
        assert actual == string_out
        assert isinstance(actual, unicode)

    @pytest.mark.skipif(False, reason='just because')
    @pytest.mark.parametrize('string_in,string_out',
                                [('Aγaπη', u'Αγαπη'),
                                 ('deλτα', u'δελτα'),
                                 ('ZEΔ', u'ΖΕΔ'),
                                 ('ΔH', u'ΔΗ'),
                                 ('τivα', u'τινα'),
                                 ('TIΣ', u'ΤΙΣ'),
                                 ('kαππα', u'καππα'),
                                 ('KΑΠΠΑ', u'ΚΑΠΠΑ'),
                                 ('ΑN', u'ΑΝ'),  # TODO: Why does ἘΝ fail here?
                                 ('ἀπo', u'ἀπο'),
                                 ('ἈΠO', u'ἈΠΟ'),
                                 ('ὡpα', u'ὡρα'),
                                 ('ὩPΑ', u'ὩΡΑ'),
                                 ('tε', u'τε'),
                                 ('ἐxω', u'ἐχω'),
                                 ('ἘXΩ', u'ἘΧΩ'),
                                 ('ἐγw', u'ἐγω')
                                 ])
    def test_convert_latin_chars(self, string_in, string_out):
        """
        Unit testing function for paideia_utils.sanitize_greek()

        """
        print 'string in', string_in
        print 'expected string out', string_out
        actual = GreekNormalizer().convert_latin_chars(string_in)
        print 'actual string out', actual
        assert actual == string_out
        assert isinstance(actual, unicode)

    @pytest.mark.skipif(False, reason='just because')
    @pytest.mark.parametrize('string_in,string_out',
                                [('Aγaπη    Aγaπη ', u'Aγaπη Aγaπη'),
                                 ])
    def test_strip_extra_spaces(self, string_in, string_out):
        """
        Unit testing function for paideia_utils.sanitize_greek()

        """
        print 'string in', string_in
        print 'expected string out', string_out
        actual = GreekNormalizer().strip_extra_spaces(string_in)
        print 'actual string out', actual
        assert actual == string_out
        assert isinstance(actual, unicode)

    @pytest.mark.skipif(False, reason='just because')
    @pytest.mark.parametrize('string_in,string_out',
                                [('῾Αγaπὴ    Aγaπη, τί ἐστιv “ἀγαπη.”',
                                  u'Ἁγαπη Αγαπη, τί ἐστιν "ἀγαπη."'),
                                 ])
    def test_normalize(self, string_in, string_out):
        """
        Unit testing function for paideia_utils.sanitize_greek()

        """
        print 'string in', string_in
        print 'expected string out', string_out
        actual = GreekNormalizer().normalize(string_in)
        print 'actual string out', actual
        assert actual == string_out
        assert isinstance(actual, unicode)

@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('regex,stringdict',
                            [('^(?P<a>ἀγαπη )?ἐγω ἐστιν(?(a)| ἀγαπη)\.$',
                             {'ἀγαπη ἐγω ἐστιν.': True,
                              'ἐγω ἐστιν ἀγαπη.': True,
                              'ἀγαπη ἐγω ἐστιν ἀγαπη.': False,
                              'ἐγω ἐστιν ἀγαπη': False
                              }
                              )
                             ])
def test_check_regex(regex, stringdict):
    """
    """
    actual = check_regex(regex, stringdict.keys())
    pprint(actual)
    assert actual == stringdict

#! /usr/bin/python
# -*- coding: UTF-8 -*-
"""
 Unit tests for the modulename module

 Configuration and some fixtures
 the file tests/conftest.py
 run with py.test -xvs path/to/tests/dir

"""

import pytest
from collections import OrderedDict
#import re
#from pprint import pprint
from greek_parser import NounPhrase, Noun, tokenize


class TestNoun():
    """
    """
    @pytest.mark.skipif(False, reason='just because')
    @pytest.mark.parametrize('nominal,string,expected',
            [('ἀρτον',  # nominal
              'Τον ἀρτον ὁ ἀνηρ πωλει.',  # string
             ([OrderedDict([('τον', None),
                            ('ἀρτον', ['Noun']),
                            ('ὁ', None),
                            ('ἀνηρ', None),
                            ('πωλει', None)])
               ],
              [])
              ),
             ])
    def test_find_article(self, nominal, string, expected):
        """
        """
        tkns = tokenize(string)[0]
        actual = Noun('ἀρτον|λογον', ['top']).match_string([tkns], [])
        print actual
        assert actual == expected


class TestNounPhrase():
    """
    """
    @pytest.mark.skipif(False, reason='just because')
    @pytest.mark.parametrize('nominal,string,expected',
            [('ἀρτον', 'Τον ἀρτον ὁ ἀνηρ πωλει.', 'pass'),
             ])
    def test_find_article(self, nominal, string, expected):
        """
        """
        tkns = tokenize(string)[0]
        actual = NounPhrase([tkns]).find_article()


'''
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('string,expected',
        [('Τον ἀρτον ὁ ἀνηρ πωλει.', 'pass'),
         ('Ὁ ἀνηρ πωλει τον ἀρτον.', 'pass'),
         ('Πωλει τον ἀρτον ὁ ἀνηρ.', 'pass'),
         ('τον πωλει ὁ ἀρτον ἀνηρ.', 'fail'),
         ('ὁ ἀρτον πωλει τον ἀνηρ.', 'fail'),
         ('ὁ τον ἀρτον πωλει ἀνηρ.', 'fail'),
         ('ὁ πωλει τον ἀρτον ἀνηρ.', 'fail')
         ])
def test_main(string, expected):
    """
    """
    pattern = Clause(None,
                     Subject(None, Noun(r'ἀνηρ'), 'def'),
                     Verb(r'πωλει|ἀγοραζει'),
                     DirObject(None, Noun(r'ἀρτον'), 'def'),
                     'top')
    actual = main(string, pattern)
    assert actual == expected
'''

Reply via email to