Python Nose2 test does not end when class method is called

When I run my tests, which include a call @classmethodusing setuptools and nose2, the test suite does not end, it just continues to work. However, I checked that the test really passes and reaches the end of the function, the test suite just does not end there. If I remove the tests with decode_auth_token, it works fine. I was able to narrow it down to class methods because I also tested other class methods and they cause the same problem.

Can anyone understand why this could happen? Below are the relevant code snippets without sending too much of my code.

Code in my user model

  @classmethod
  def decode_auth_token(cls, auth_token):
    try:
      payload = jwt.decode(auth_token, config.SECRET_KEY, algorithms=['HS256'])
      # check the hash of what we expect the token to be and token we got to be the same
      if bcrypt.check_password_hash(User.by_id(payload['sub']).api_token_hash, auth_token):
        return payload['sub']
      else:
        return 'Token does not match Api Token.'
    except jwt.ExpiredSignatureError:
      return 'Signature expired. Please log in again.'
    except jwt.InvalidTokenError:
      return 'Invalid Token. Please log in again.'

The following two functions also cause a problem when called

  @classmethod
  def is_username_taken(cls, username):
    return db.session.query(db.exists().where(User.username==username)).scalar()

  @classmethod
  def is_email_taken(cls, email):
    return db.session.query(db.exists().where(User.email==email)).scalar()

This function does not cause a problem when called though

@classmethod
def by_username(cls, username):
  return User.query.filter(User.username == username).first()

Here are the tests

import unittest
import sys

from . import AppTestCase, API_ROOT
from app.extensions import db, bcrypt
from app.models import User, UserSchema, Location, Company


class TestUserModel(AppTestCase):
  def test_encode_auth_token(self):
    user = User.by_username('jdoe')
    auth_token = user.encode_auth_token(user.id)
    self.assertTrue(isinstance(auth_token, bytes))

  def test_decode_auth_token(self):
    user = User.by_username('jdoe')
    auth_token = user.encode_auth_token(user.id)
    self.assertTrue(isinstance(auth_token, bytes))
    self.assertEqual(User.decode_auth_token(auth_token), user.id)
    print('DONE')

, Done auth_token, , . . ​​

script, , python setup.py test

import os
from setuptools import setup, find_packages, Command

# Thanks http://stackoverflow.com/questions/3779915/why-does-python-setup-py-sdist-create-unwanted-project-egg-info-in-project-r
class CleanCommand(Command):
  """Custom clean command to tidy up the project root."""
  user_options = []
  def initialize_options(self):
    pass
  def finalize_options(self):
    pass
  def run(self):
    os.system('rm -vrf ./build ./dist ./*.pyc ./*.tgz ./*.egg-info')

with open('requirements.txt') as f:
    requirements = f.read().splitlines()

setup(
  name="XXX",
  description="XXX",
  version=1.0,
  packages=find_packages(),
  install_requires=requirements,
  include_package_data=True,
  test_suite='nose2.collector.collector',
  tests_require=['nose2'],
  cmdclass={
        'clean': CleanCommand,
    }
)

running test
Searching for nose2
Best match: nose2 0.6.5
Processing nose2-0.6.5-py3.6.egg

Using XXX/.eggs/nose2-0.6.5-py3.6.egg
running egg_info
writing doomfist.egg-info/PKG-INFO
writing dependency_links to XXX.egg-info/dependency_links.txt
writing requirements to XXX.egg-info/requires.txt
writing top-level names to XXX.egg-info/top_level.txt
reading manifest file 'XXX.egg-info/SOURCES.txt'
writing manifest file 'XXX.egg-info/SOURCES.txt'
running build_ext
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/parser.py:50: DeprecationWarning: invalid escape sequence \.
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/parser.py:50: DeprecationWarning: invalid escape sequence \.
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/tz/win.py:197: DeprecationWarning: invalid escape sequence \{
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/tz/win.py:247: DeprecationWarning: invalid escape sequence \{
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/tz/win.py:197: DeprecationWarning: invalid escape sequence \{
/Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/python_dateutil-2.6.0-py3.6.egg/dateutil/tz/win.py:247: DeprecationWarning: invalid escape sequence \{
NOT running in debug mode
DONE
^]^\[1]    35752 quit       python setup.py test

EDIT ----- - , , . tearDown(). - , .

def tearDown(self):
    """Clean db session and drop all tables."""
    db.drop_all()

,

for table, fkcs in collection:
  if table is not None:
    self.traverse_single(table, drop_ok=True, _is_metadata_operation=True)
  else:
    for fkc in fkcs:
    ...

, self.traverse_single(table, drop_ok=True, _is_metadata_operation=True). , , ? , - , , .

> /Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/SQLAlchemy-1.1.11-py3.6-macosx-10.7-x86_64.egg/sqlalchemy/sql/ddl.py(929)visit_table()->None
-> _is_metadata_operation=_is_metadata_operation)
(Pdb) n
--Call--
> /Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/SQLAlchemy-1.1.11-py3.6-macosx-10.7-x86_64.egg/sqlalchemy/sql/visitors.py(150)_visitor_iterator()-><sqlalchemy.s...t 0x112045630>
-> yield v
(Pdb) n
GeneratorExit
> /Users/XXX/anaconda3/envs/XXX/lib/python3.6/site-packages/SQLAlchemy-1.1.11-py3.6-macosx-10.7-x86_64.egg/sqlalchemy/sql/visitors.py(150)_visitor_iterator()-><sqlalchemy.s...t 0x112045630>
-> yield v
(Pdb) l
145         def _visitor_iterator(self):
146             """iterate through this visitor and each 'chained' visitor."""
147     
148             v = self
149             while v:
150  ->             yield v
151                 v = getattr(v, '_next', None)
152     
153         def chain(self, visitor):
154             """'chain' an additional ClauseVisitor onto this ClauseVisitor.
155     
(Pdb) n

,

from ..helpers import get_current_time
from ..extensions import db, ma
from ..constants import STRING_LEN, DESCRIPTION_LEN
from .worker import WorkerSchema

class Injury(db.Model):

  __tablename__ = "injuries"
  def __repr__(self):
    return '<Injury %r>' % (self.id)

  id            = db.Column(db.Integer, primary_key = True)
  title         = db.Column(db.String(STRING_LEN), nullable=False)
  description   = db.Column(db.String(DESCRIPTION_LEN), nullable=False)
  worker_id     = db.Column(db.Integer, db.ForeignKey('workers.id'))
  created_at    = db.Column(db.DateTime, nullable=False, default = get_current_time)
  updated_at    = db.Column(db.DateTime, nullable=False, default = get_current_time, onupdate=get_current_time)

  # Relationships
  worker = db.relationship('Worker', back_populates='injuries')

  # ================================================================

  # ================================================================
  # methods


  # ================================================================
  # Class methods

  @classmethod
  def by_id(cls, id):
    return cls.query.filter(Injury.id==id).first()

class InjurySchema(ma.Schema):
  class Meta:
    fields = ('id', 'title', 'description', 'worker')

  worker = ma.Nested(WorkerSchema)
+6
1

, db.session.close() , drop_all SQLAlchemy

def tearDown(self):
    """Clean db session and drop all tables."""
    db.session.close()
    db.drop_all()

, ,

+4

All Articles