0
votes

I had only one big file serving my application app.py, but now, I decided to split into several files to separate the code. After this separation, the WSGI doesn't start anymore with an error related to import a function.

ImportError: cannot import name month_string_to_number

The code now is divided into 3 main files: app.py, views.py and models.py. Here you have some code, as well as the Apache and WSGI configuration.

app.py

import sqlite3
import json
import os
import csv
import codecs
from flask import Flask, jsonify, g, request, abort, render_template, redirect, url_for, flash
from flask_sqlalchemy import Model, SQLAlchemy
from sqlalchemy import text, and_
from datetime import datetime, timedelta
from werkzeug import secure_filename
from flask_login import LoginManager


app = Flask(__name__)
login = LoginManager(app)
login.login_view = 'login'
app.secret_key = 'certificates-management'

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///certificates.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

dir_path = os.path.dirname(os.path.realpath(__file__))

db = SQLAlchemy(app)

from views import *

def month_string_to_number(string):
    m = {
            'jan': '1',
            'feb': '2',
            'mar': '3',
            'apr': '4',
            'may': '5',
            'jun': '6',
            'jul': '7',
            'aug': '8',
            'sep': '9',
            'oct': '10',
            'nov': '11',
            'dec': '12'
        }
    s = string.strip()[:3].lower()

    try:
        out = m[s]
        return out
    except:
        raise ValueError('Not a month')

if __name__ == '__main__':
    app.run(debug=True)

views.py

import sqlite3
import json
import os
import csv
import codecs
from flask import Flask, jsonify, g, request, abort, render_template, redirect, url_for, flash
from flask_sqlalchemy import Model, SQLAlchemy
from models import Worker, Certificates, Logs, User, return_workers, insert_db, create_log_entry
from sqlalchemy import text, and_
from datetime import datetime, timedelta
from werkzeug import secure_filename
from app import app, db, month_string_to_number
from flask_login import current_user, login_user, logout_user, login_required
from urlparse import urlparse

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        if current_user.is_authenticated:
            return redirect(url_for('index'))
        user_name = request.form['username']
        user_password = request.form['password']
        remember_me = request.form.get('remember_me', False)
        user = User.query.filter_by(username=user_name).first()
        dir(user)
        if user is None or not user.check_password(user_password):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        login_user(user, remember=remember_me)
        create_log_entry(current_user.name, "Logged in to the system", "Login")
        flash("Login successfull", 'success')
        return redirect(url_for('index'))
    return render_template('login.html')

@app.route('/logout')
def logout():
    create_log_entry(current_user.name, "Logged out from the system", "Logout")
    logout_user()
    flash("Logged out successfully", 'success')
    return redirect(url_for('index'))

models.py

from app import app, db, login
import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    username = db.Column(db.String(255), nullable=False, unique=True)
    password_hash = db.Column(db.String(255), nullable=False)

    def __init__(self, username, password):
        self.username = username
        self.password_hash = password

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

The WSGI code is this:

import sys
sys.path.append("/var/www/certs-management")
from app import app as application

And the Apache config:

<virtualhost *:80>
    ServerName myserver.example

    WSGIDaemonProcess certs-management user=apache group=apache threads=5 home=/var/www/certs-management/
    WSGIScriptAlias / /var/www/certs-management/cert-management.wsgi

    Alias /static /var/www/certs-management/static/
     <Directory "/var/www/certs-management/static/">
      Order allow,deny
      Allow from all
    </Directory>
    Alias /templates /var/www/certs-management/templates/
    <Directory "/var/www/certs-management/templates/">
      Order allow,deny
      Allow from all
    </Directory>
    <directory /var/www/certs-management>
        WSGIProcessGroup certs-management
        WSGIApplicationGroup %{GLOBAL}
        WSGIScriptReloading On
        Order deny,allow
        Allow from all
    </directory>
</virtualhost>

The error message is the following:

[Wed Jan 31 18:59:48.397516 2018] [mpm_prefork:notice] [pid 5634] AH00171: Graceful restart requested, doing restart
[Wed Jan 31 18:59:48.546219 2018] [auth_digest:notice] [pid 5634] AH01757: generating secret for digest authentication ...
[Wed Jan 31 18:59:48.546904 2018] [lbmethod_heartbeat:notice] [pid 5634] AH02282: No slotmem from mod_heartmonitor
[Wed Jan 31 18:59:48.547518 2018] [mpm_prefork:notice] [pid 5634] AH00163: Apache/2.4.6 (Red Hat Enterprise Linux) mod_wsgi/3.4 Python/2.7.5 configured -- resuming normal operations
[Wed Jan 31 18:59:48.547538 2018] [core:notice] [pid 5634] AH00094: Command line: '/usr/sbin/httpd'
[Wed Jan 31 18:59:52.011929 2018] [:error] [pid 3881] [remote :176] mod_wsgi (pid=3881): Target WSGI script '/var/www/certs-management/cert-management.wsgi' cannot be loaded as Python module.
[Wed Jan 31 18:59:52.011985 2018] [:error] [pid 3881] [remote :176] mod_wsgi (pid=3881): Exception occurred processing WSGI script '/var/www/certs-management/cert-management.wsgi'.
[Wed Jan 31 18:59:52.012015 2018] [:error] [pid 3881] [remote :176] Traceback (most recent call last):
[Wed Jan 31 18:59:52.012033 2018] [:error] [pid 3881] [remote :176]   File "/var/www/certs-management/cert-management.wsgi", line 3, in <module>
[Wed Jan 31 18:59:52.012098 2018] [:error] [pid 3881] [remote :176]     from app import app as application
[Wed Jan 31 18:59:52.012111 2018] [:error] [pid 3881] [remote :176]   File "/var/www/certs-management/app.py", line 26, in <module>
[Wed Jan 31 18:59:52.012158 2018] [:error] [pid 3881] [remote :176]     from views import *
[Wed Jan 31 18:59:52.012169 2018] [:error] [pid 3881] [remote :176]   File "/var/www/certs-management/views.py", line 12, in <module>
[Wed Jan 31 18:59:52.012320 2018] [:error] [pid 3881] [remote :176]     from app import app, db, month_string_to_number
[Wed Jan 31 18:59:52.012347 2018] [:error] [pid 3881] [remote :176] ImportError: cannot import name month_string_to_number

I really have no idea why it's happening. To configure this app, I just followed the documentation at: http://flask.pocoo.org/docs/0.12/deploying/mod_wsgi/

PS: The server name is not right and the information about IP of remote host was also removed - which I am sure are not relevant to the issue.

Thank you.

2
Does look like it should work. Whenever I get something that "should work" I always delete all my *.pyc and *.pyo flies just in case. Every now and then, it saves me a lot of time stuck in befuddlement.BareNakedCoder
@BareNakedCoder I tried your suggestion, but it didn't work. Thanks anyway.Ricardo Silva

2 Answers

1
votes

It doesn't look like a problem with WSGI or Apache, but the fact that you have circular imports. In your app.py, you're importing everything from views.py:

from views import *

And in views.py, you import back from app.py:

from app import app, db, month_string_to_number

The real problem is that you're importing using from ... import .... If you instead use import views and then refer to objects as views.xyz, it should work. See this response on Reddit in case you're intested in understanding why.

Please let us know if this solves your issue or if there's another problem going on.

0
votes

After checking Renato's suggestion, I did something different to solve the issue. Indeed it was the circular import the real issue, not WSGI, not Apache.

Instead of having one function in app.py being called from views.py, I moved this function month_string_to_number to views.py and removed the import from app.py into views.py.

It solved the issue.

If you're facing any issues like this, you should check your circular imports to be sure you're not making this mistake.