3

Consider this simple folder structure:

root
  Package1
    x.py
    y.py
  Package2
    z.py
  Examples
    main.py

Now our requirements are:

  • x.py needs to import y.py
  • z.py needs to import y.py
  • main.py needs to import y.py and z.py

Below is what works:

x.py

import y

def x():
  y()

y.py

def y():
  pass

z.py

import package1.y as y

def z():
  y.y()

main.py

import sys
from os import path
sys.path.append(  path.dirname(  path.dirname( path.abspath(__file__) ) ) )

import package1.y as y
import package2.z as z

y.y()
z.z()

Questions:

  1. Is this the best and recommended way to setup imports in Python 3?
  2. I really don't like changing sys.path in main because it strongly binds assumptions about package locations inside code file. Is there any way around that?
  3. I also really don't like superfluous as y part in import package1.y as y. Is there any way around that?
Shital Shah
  • 63,284
  • 17
  • 238
  • 185
  • Just to make sure I understand, `root` is not a package. You have two independent packages called `Package1` and `Package2`. The `sys.path` option is bad because it means I can't clone different copies of this package for development. I think the best option is to create `setup.py` files for each package and make them installable. Now someone could install, install into a virtualenv, or set their own path outside of any tests to where ever they happen to be using them. Make it easy for end users who just want to install. – tdelaney May 05 '18 at 05:12
  • ...but also easy for developers with a little extra knowledge to use them. – tdelaney May 05 '18 at 05:13
  • `x.py` could do `import .y` but `z.py` is in an entirely separate package. it needs the full `Package1.y`. – tdelaney May 05 '18 at 05:14
  • Yes, root is not package. Question is how source code should look on disk during the development? – Shital Shah May 05 '18 at 08:57
  • Good question! Its common for each to have their own source repository and not have any hardcoded relationship to each other. Users would `pip install` them and developers would either either `pip install --editable` or `pip install --develop` them. Even if they are in the same repository of other reasons, they are still separate python entities. (This is all my humble opinion of course) – tdelaney May 05 '18 at 15:17

1 Answers1

1

As always, there are two separate steps:

  1. You write the code against the abstract namespace of packages, which contains package1 and package2 (and sys, os, etc.), but not “Examples” which is not a package (because main.py is not a module).
  2. You set sys.path appropriately before any of your code ever runs. If it's your own (uninstalled) code, there are places you can put it, or you can write an easy shell script wrapper to set PYTHONPATH for your python process.

So the answers to your questions are

  1. In x.py you write from . import y. (Python 2 supports this and 3 requires it.)
  2. How you set sys.path depends on your packaging/environment system. The traditional way is to set the PYTHONPATH environment variable for the python process, but there are other ways involving things like the site module.
  3. from package1 import y is the usual way to name things only once.
Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • NB for step 2: "install your package" is a valid way of setting up `sys.path` :) – ash May 11 '18 at 23:22
  • 1
    @Josh: Sure, if your environment supports “installation”; lots of people use virtual environments and so forth, but some of us have to put packages in non-default locations and use external configuration like [Environment Modules](https://en.m.wikipedia.org/wiki/Environment_Modules_(software)). It’s useful to understand the basic mechanisms in any event. – Davis Herring May 11 '18 at 23:25
  • There shouldn't be any need to install package (user can if they wish, but best practice should not require it). This is especially important if you are in development mode and may be you just want you package to be copied in private folder with your own version without effecting system level package. – Shital Shah May 12 '18 at 00:41
  • In x.y, why should one write `from . import y` instead of just `import y`. Former is verbose and is not actually required by Python 3. – Shital Shah May 12 '18 at 01:54
  • @ShitalShah: If `x` and `y` are in a package, it’s certainly required. If you have the directory `Package1` (rather than its parent) on `sys.path`, then there is no package, but that’s not what you seem to want. – Davis Herring May 12 '18 at 02:14
  • @DavisHerring The code mentioned in question works as expected in Python 3. In Python 2 we neede_i_init__.py for packages but that's not the case in Python 3. In above code, we added the parent directory of package in sys.path (not the package directory itself). – Shital Shah May 14 '18 at 20:19
  • @ShitalShah: [What?](https://wandbox.org/permlink/3Y0AEufVnvPspcs0) Everyone [knows](https://softwareengineering.stackexchange.com/a/159505) the `from .` is required in 3. – Davis Herring May 15 '18 at 00:56
  • @DavisHerring - why don't you try it out: https://repl.it/@sytelus/comment8769798750301348. Specifically see import inside package1/x.py. – Shital Shah May 15 '18 at 02:26
  • @ShitalShah: That example never imports `package1.x`, which contains the invalid `import y`. (Also, the `sys.path` entry added is irrelevant.) – Davis Herring May 15 '18 at 03:15
  • @DavisHerring I see. I had made error of not using x.py in the example. Ok... I've marked your answer as accepted. – Shital Shah May 15 '18 at 04:22