How to reference python package when filename contains a period
Asked Answered
C

7

57

I am using django and I have a file named models.admin.py and I want to do the following idea in models.py:

from "models.admin" import *

however, I get a syntax error for having double quotes. But if I just do

from models.admin import *

then I get "ImportError: No module named admin"

Is there any way to import from a python file that has a period in its name?

Corinacorine answered 1/12, 2009 at 18:53 Comment(1)
Technically, that's a module, not a package.Springbok
U
44

Actually, you can import a module with an invalid name. But you'll need to use imp for that, e.g. assuming file is named models.admin.py, you could do

import imp
with open('models.admin.py', 'rb') as fp:
    models_admin = imp.load_module(
        'models_admin', fp, 'models.admin.py',
        ('.py', 'rb', imp.PY_SOURCE)
    )

But read the docs on imp.find_module and imp.load_module before you start using it.

Uredo answered 1/12, 2009 at 19:16 Comment(9)
Bad practice? imp is the mechanism behind import and __import__. More flexibility, but requires more code. Nothing magical about it, though.Uredo
And machine code is the mechanism behind everything. More flexibility and.. see where I'm going? :)Brainstorm
You obviously need to think twice if you really need to use it, but I don't see how using it is terribly wrong. ;) And it is used in production - see e.g. Trac - trac.edgewall.org/browser/trunk/trac/loader.py.Uredo
My main point is, that, perhaps it is not terribly wrong to use this (ok, my subjective opinion is that it is terribly wrong, but away with that), but it is certainly not particularly right to advise it to someone who doesn't quite grasp the concept of modules and packages yet.Brainstorm
For the record, I never actually named my file that the instant I realized this was going to be complicated. But after Googling out of curiosity and failing to find the answer, I decided that I wanted both the answer (import imp) and the voice of accepted practice (don't use it) to be archived for future Googlers. :)Corinacorine
I think the important point is that this is an invalid module name as per python style guide: python.org/dev/peps/pep-0008. However, in OP's case, if module name can't be changed, this solution seems like it is reasonable, as it uses well documented interface. In other words, it is unlikely for a future python minor release to change this behavor. If the solution involved undocumented features, it is a different matter and I would have agreed with shylent.Gentianaceous
To try and make a case for this method of import, consider a naming convention for unit tests whereby code related to testing module x is kept in a file called 'x.test.py'. This works fine most of the time since the unit tests are not normally imported by other modules but if a developer wants to reuse the test for 'x' as the basis for additional testing (ie...maybe they want to test mutlithreaded use of x in a file called 'mtx.test.py') then they would find themselves needing to import 'x.test' which of course only works with the help of the 'imp' module.Misdirection
This is formally deprecated as of 3.3. The correct way is to fiddle with the classes in importlib.machinery, which IMHO is way more trouble than it's worth.Allelomorph
I am working with a library where a version number has to be in the directory structure (not my choice). Since I have no choice, I need something like this so thanks for this answer!Ilocano
G
15

If you really want to, you can import a module with an unusual filename (e.g., a filename containing a '.' before the '.py') using the imp module:

>>> import imp
>>> a_b = imp.load_source('a.b', 'a.b.py')
>>> a_b.x
"I was defined in a.b.py!"

However, that's generally a bad idea. It's more likely that you're trying to use packages, in which case you should create a directory named "a", containing a file named "b.py"; and then "import a.b" will load a/b.py.

Gearldinegearshift answered 1/12, 2009 at 19:15 Comment(2)
load_source is obsolete since at least 1.5.Uredo
True, though the non-obsolete way of doing it is quite a bit wordier: a_b = imp.load_module('a.b', open('a.b.py'), os.path.abspath('a.b.py'), ('.py', 'r', imp.PY_SOURCE))Gearldinegearshift
S
4

The file is called models/admin.py. (Source)

That is, it should be called admin.py in a directory called models.

Then you can import using from models.admin import *, assuming that it is in your Python path.

Shredding answered 1/12, 2009 at 18:58 Comment(0)
T
4

Like below

Assume dir structure is like this:

C:.
│   script.py
│   
└───Parent
    └───Child
        ├───1.1
        │       main.py
        │       
        └───1.2

**assume you want to import main.py in script.py **

your main.py looks like below

def my_function():
  print("Hello from a function")

your script.py looks like below

from os import path
import importlib
from os.path import dirname
import sys
import importlib.util


def getPath():
    # your logic to get to the path
    return path.join(dirname(__file__),'Parent','Child','1.1','main.py')

file_path = getPath() 
module_name = 'main'

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

#call functions like this
module.my_function()

Check out this gist

Telescopium answered 20/11, 2021 at 6:29 Comment(0)
H
3

No, you can't import a python file as a module if its name contains a period (or a question mark, or exclamation mark, etc). A python module's name (not including the .py) must be a valid python name (ie can be used as a variable name).

Hepsibah answered 1/12, 2009 at 19:3 Comment(4)
Do you have a reference handy for this?Methoxychlor
The reference is the Python grammar spec (docs.python.org/2/reference/grammar.html). However, this actually only specifies what is allowed for the import statement -- as pointed out above in the accepted answer, it's technically possible to get around this by using the underlying import mechanisms.Hepsibah
What would be the best pattern for long names with multiple words? Naming a package, and thus a folder "awesome.project" will create issues, while "awesomeProject" seems an anti-pattern. Not sure I get this rule correctly: python.org/dev/peps/pep-0423/#id87Indo
Actually you can, see @SciTech Enthusiast answerInspan
T
1

In my case, I am using grafanalib, and the filename has to be xx.dashboard.py based on the doc. However, I do want to import this file to simplify the uploading step.

I got warning when I use import imp:

the imp module is deprecated in favour of importlib and slated for removal in Python 3.12; see the module's documentation for alternative uses

Here is the simple demo using importlib and pathlib:

foo.bar.py and main.py are in the same foler.

# foo.bar.py

num = 42
# main.py

import importlib.machinery
import pathlib

module = importlib.machinery.SourceFileLoader(
    "foo_bar",
    pathlib.Path(__file__).parent.joinpath("foo.bar.py").resolve().as_posix(),
).load_module()
print(module.num)  # 42
Tiliaceous answered 29/11, 2022 at 6:8 Comment(0)
B
-1

You are not referencing files in the import statement, you are referencing modules and packages.

Please read the docs, they are very clear on that matter.

Anyway, since you are using django, the usual approach won't work. If you want to keep models in separate files, rather than in models.py, you have to take extra steps, outlined, for example, here.

Edit:
Well, I don't really know what the questioneer means when he mentions admin and whether or not it is related to the admin interface of django. My points still stand.

Brainstorm answered 1/12, 2009 at 19:5 Comment(1)
A module is a file. To quote the docs you referenced: To support this, Python has a way to put definitions in a file and use them in a script or in an interactive instance of the interpreter. Such a file is called a module;Corinacorine

© 2022 - 2024 — McMap. All rights reserved.