Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it OK to execute code when a module imports?

I'm designing a small GUI application to wrap an sqlite DB (simple CRUD operations). I have created three sqlalchemy models (m_person, m_card.py, m_loan.py, all in a /models folder) and had previously had the following code at the top of each one:

from sqlalchemy import Table, Column, create_engine
from sqlalchemy import Integer, ForeignKey, String, Unicode
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, relation

engine = create_engine("sqlite:///devdata.db", echo=True)
declarative_base = declarative_base(engine)
metadata = declarative_base.metadata

This felt a bit wrong (DRY) so it was suggested that I move all this stuff out to the module level (into models/__init__.py).

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Table, Column, Boolean, Unicode
from settings import setup

engine = create_engine('sqlite:///' + setup.get_db_path(), echo=False)
declarative_base = declarative_base(engine)
metadata = declarative_base.metadata

session = sessionmaker()
session = session()

..and import declarative_base like so:

from sqlalchemy import Table, Column, Unicode
from models import declarative_base


class Person(declarative_base):
    """
    Person model

    """
    __tablename__ = "people"

    id = Column(Unicode(50), primary_key=True)
    fname = Column(Unicode(50))
    sname = Column(Unicode(50))

However I've had a lot of feedback that executing code as the module imports like this is bad? I'm looking for a definitive answer on the right way to do it as it seems like by trying to remove code repetition I've introduced some other bad practises. Any feedback would be really useful.

(Below is the get_db_path() method from settings/setup.py for completeness as it is called in the above models/__init__.py code.)

def get_db_path():

    import sys
    from os import makedirs
    from os.path import join, dirname, exists
    from constants import DB_FILE, DB_FOLDER

    if len(sys.argv) > 1:
        db_path = sys.argv[1]
    else:
        #Check if application is running from exe or .py and adjust db path accordingly
        if getattr(sys, 'frozen', False):
            application_path = dirname(sys.executable)
            db_path = join(application_path, DB_FOLDER, DB_FILE)
        elif __file__:
            application_path = dirname(__file__)
            db_path = join(application_path, '..', DB_FOLDER, DB_FILE)

    #Check db path exists and create it if not
    def ensure_directory(db_path):
        dir = dirname(db_path)
        if not exists(dir):
            makedirs(dir)

    ensure_directory(db_path)

    return db_path
like image 963
johnharris85 Avatar asked Feb 28 '12 17:02

johnharris85


2 Answers

In principle there is nothing wrong with executing Python code when a module is imported, in fact every single Python module works that way. Defining module members is executing code, after all.

However, in your particular use case I would strongly advise against making a singleton database session object in your code base. You'll be losing out on the ability to do many things, for example unit test your model against an in-memory SQLite or other kind of database engine.

Take a look at the documentation for declarative_base and note how the examples are able to create the model with a declarative_base supplied class that's not yet bound to a database engine. Ideally you want to do that and then have some kind of connection function or class that will manage creating a session and then bind it to base.

like image 84
Kamil Kisiel Avatar answered Oct 05 '22 20:10

Kamil Kisiel


There is nothing wrong with executing code at import time.

The guideline is executing enough so that your module is usable, but not so much that importing it is unnecessarily slow, and not so much that you are unnecessarily limiting what can be done with it.

Typically this means defining classes, functions, and global names (actually module level -- bit of a misnomer there), as well as importing anything your classes, functions, etc., need to operate.

This does not usually involve making connections to databases, websites, or other external, dynamic resources, but rather supplying a function to establish those connections when the module user is ready to do so.

like image 29
Ethan Furman Avatar answered Oct 05 '22 22:10

Ethan Furman