11

I need assistance on how to organize source in a python package - I've already followed several tutorials on the web (especially this one) on how to do so, but it does not work as explained and how I imagined it.

I want to create a python package named binaryio. It should offer two classes named BinaryReader and BinaryWriter which I want users to be able to import with

from binaryio import BinaryReader
from binaryio import BinaryWriter

Thus I have created my repository and package directory structure as follows:

  • binaryio (repository root)
    • binaryio (package root)
      • __init__.py (s. below)
      • binaryreader.py (contains the BinaryReader class)
      • binarywriter.py (contains the BinaryWriter class)
    • setup.py (contains the setuptools.setup call)
    • .gitignore, README.md, LICENSE, ...

As you can see, the classes are in separate files as I'm used to this (coming from a C# background). I'm not sure if this is a good idea due to modules being the "unit" in Python - but otherwise cramping all classes into one huge file did not seem logical to me.

__init__.py looks as follows to import those classes, making (as I understood it) the from binaryio import BinaryReader imports possible for users later on:

from binaryreader import BinaryReader
from binarywriter import BinaryWriter

However, when I install the package locally (which seems to work fine) and try to import binaryio, I get the following error:

>>> import binaryio
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\Projects\Git\binaryio\binaryio\__init__.py", line 1, in <module>
    from binaryreader import BinaryReader
ModuleNotFoundError: No module named 'binaryreader'

Apparently, something is wrong with my __init__.py file. I don't understand this, as a binaryreader.py file aka module exists in the same folder as you can see above. Funnily enough, my IDE (PyCharm, having set the package root as source folder) does not complain about the statements in it and can resolve all references.

What am I doing wrong here? According to the tutorial linked above, putting a file into xyz.py with a class named Abc and then write from xyz import Abc into __init__.py should work, but apparently it doesn't for me.

Ray
  • 7,940
  • 7
  • 58
  • 90

2 Answers2

10

Your code would work for Python 2.x, but not 3.x because of different relative imports syntax: without dot, Python 2.x would look for modules in module root and current package, and Python 3.x will look only in module root.

Import statements you want to use are these:

from binaryio.binaryreader import BinaryReader
from binaryio.binarywriter import BinaryWriter

Works in both Python 2.x and 3.x without "futures"

bakatrouble
  • 1,746
  • 13
  • 19
  • Does that mean Xatyrian's answer with only the leading dot is equivalent to this, as **\__init\__.py** is already in the **binaryio** folder? Or is that syntax not working in Python 2 at all? – Ray Jun 28 '17 at 12:14
  • 1
    Yes, it is almost equivalent. The differences are: the Xatyrian's snippet would require `from __future__ import absolute_import` for Python 2.x and my answer wouldn't work if `binaryio` package is not located in the module root. – bakatrouble Jun 28 '17 at 12:17
  • 1
    Thanks, I suspected that due to your last statement about "futures". I accept your reply as the answer as it explains the reasoning behind it. – Ray Jun 28 '17 at 12:18
7

I think you need to add a dot in your import statements :

from .binaryreader import BinaryReader
from .binarywriter import BinaryWriter
Xatyrian
  • 1,364
  • 8
  • 26
  • That worked perfectly. But now I'm really interested why **I** have to explicitly name the current directory with the dot, and all other examples I found on the web didn't. And why it is actually a difference to the non-dot version (looking that up now, as nobody has told me there _is_ a difference...) – Ray Jun 28 '17 at 12:05
  • 2
    it's a difference between python 2 and python 3. – Jean-François Fabre Jun 28 '17 at 12:07
  • Oh hell, I fell into the "Tutorials on the web didn't specify Python version" trap again :( Thanks for clearing that up! – Ray Jun 28 '17 at 12:12