Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

change in import handling / modules from python2 to python3?

I was trying to following the design pattern shown in this previous question related to SQLAlchemy and intended to share a common Base instance across multiple files. The code exactly as is works on python2 and python3.

However, when I move the files a.py, b.py, c.py, and base.py in a module (called model) and add the necessary __init__.py file, it continues to work on python2 but then produces an error on python3 (details below).

I have the following files:

model/base.py

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

model/a.py

from sqlalchemy import *
from base import Base
from sqlalchemy.orm import relationship

class A(Base):
    __tablename__ = "A"
    id  = Column(Integer, primary_key=True)
    Bs  = relationship("B", backref="A.id")
    Cs  = relationship("C", backref="A.id")

model/b.py

from sqlalchemy import *
from base import Base

class B(Base):
    __tablename__ = "B"
    id    = Column(Integer, primary_key=True)
    A_id  = Column(Integer, ForeignKey("A.id"))

model/c.py

from sqlalchemy import *
from base import Base

class C(Base):
    __tablename__ = "C"    
    id    = Column(Integer, primary_key=True)
    A_id  = Column(Integer, ForeignKey("A.id"))

model/__init__.py

(empty)

main.py

from sqlalchemy import create_engine
from sqlalchemy.orm import relationship, backref, sessionmaker

from model import base


from model import a
from model import b
from model import c

engine = create_engine("sqlite:///:memory:")
base.Base.metadata.create_all(engine, checkfirst=True)
Session = sessionmaker(bind=engine)
session = Session()

a1 = a.A()
b1 = b.B()
b2 = b.B()
c1 = c.C()
c2 = c.C()

a1.Bs.append(b1)
a1.Bs.append(b2)    
a1.Cs.append(c1)
a1.Cs.append(c2)    
session.add(a1)
session.commit()

python2 works:

$ python main.py ; echo $?
0

python3 errs with:

$ python3 main.py ; echo $?
Traceback (most recent call last):
  File "main.py", line 7, in <module>
    from model import a
  File "/home/shale/code/py/try/model/a.py", line 2, in <module>
    from base import Base
ImportError: No module named base
1

I ultimately solved this by putting the code from base.py into my __init__.py file (described as one answer below), but does anyone know why this produces an error in python3 but not in python2? What change is responsible for this in the first place?

like image 563
computermacgyver Avatar asked Oct 28 '13 20:10

computermacgyver


People also ask

What changes in IMPORT statement in Python 3?

Changes in import statement python3. In Python 3, implicit relative imports within packages are no longer available - only absolute imports and explicit relative imports are supported. In addition, star imports (e.g. from x import *) are only permitted in module level code.

Why did Python change from Python 2 to Python 3?

The change from Python 2 to Python 3 was taken as an opportunity to "fix" some issues with Python 2. Among them promoting Unicode more uniformly throughout the language and to clear up some issues in the syntax such as print being a statement rather than a function.

Should you modernize your Python 2 code to support Python 3?

Another key point is that modernizing your Python 2 code to also support Python 3 is largely automated for you. While you might have to make some API decisions thanks to Python 3 clarifying text data versus binary data, the lower-level work is now mostly done for you and thus can at least benefit from the automated changes immediately.

How do I port my code from Python 2 to 3?

Another way to help port your code is to use a static type checker like mypy or pytype on your code. These tools can be used to analyze your code as if it’s being run under Python 2, then you can run the tool a second time as if your code is running under Python 3.


1 Answers

Python 3 switches to absolute imports by default, and disallows unqualified relative imports. The from base import Base line is such an import.

Python 3 will only look for top-level modules; you don't have a base top-level module, only model.base. Use a full module path, or use relative qualifiers:

from .base import Base

The . at the start tells Python 3 to import starting from the current package.

You can enable the same behaviour in Python 2 by adding:

from __future__ import absolute_import

This is a change introduced by PEP 328, and the from future import is available from Python 2.5 onwards.

like image 132
Martijn Pieters Avatar answered Oct 07 '22 02:10

Martijn Pieters