How does python resolve imports (especially in context of django)? - python

I am doing a tutorial to get to know Django.
The last chapter I did was this one http://www.tangowithdjango.com/book17/chapters/models.html
I am using a virtual-environment managed via virtualenvironmentwrapper.
What I was wondering about is, how the imports really work. For example
we have this statement:
from rango.models import Category, Page
where we work on the file admin.py which is located in the same folder .../rango/ as models.py so I would expect
from models import Category, Page
to be the import statement (which seems to work, too as far as i can test it).
Ok, .../rango/ contains an __init__.py and therefore is considered as package as far as I know but why would I use the longer import-statement over the shorter one?
But most important to me would be:
When I use the shell to see sys.path I only get the directories created by my virtualenvwrapper where i would have expected it to contain (besides others) something like
[...]/rango/..
(the path to the parent-directory of rango).
How does the interpreter know where to search for those modules/packages in the first place?

Related

Unused import statement: classic problem while importing own module

OK, prerequisites:
It's my first ever Python project. I used to do some scripting but never anything bigger
So I'm at the very beginning of a learning curve. It's like when you can't kill an ant in Fallout 2 Temple level. On later levels, I was really good in Fall 2:)
Problem:
I can't figure out how to import a module written by me and placed in a different folder
Context:
The project I'm intended to create is meant to do a lot of measures conversions. So I decided to store in DB all data in the same unit system & keep all conversions upon user preferences on a codebase level
In a different folder I decided to store tests. To write the very first one (testing the abovementioned module) I need to import the module, but here is the story begins. I know it's classic, but I'm completely messed with import
Toolkit:
PyCharm Pro (PyCharm 2021.3.1)
Python 3.7 interpreter
macOS 10.15, Intell
Set up:
Settings screenshot provided
Project structure. Folders are marked as Source & Test
I need to import from conversions.py to test_conversions.py
PYTHONPATH settings like this
What do I, for the sake of God, need:
with all the abovementioned, how do I import conversions.py to test_conversions.py or any other place of my project? I read a number of articles and it's getting me anywhere (contradictory, 2.x related, etc). I feel like I need a piece of more foundational info but as well I need a clear walkthrough, a code snippet to import bloody file, I really appreciate any kind of advice
imports are a bit tricky. The issue you have is where your python is looking for packages. I would discourage you to add to your PYTHONPATH a specific project but you could do that locally in your file.
A much easier way is just to launch your test file from the top directory.
In this case your import will just be import conversion.conversion
And then you can launch your test from the root folder with python -m tests.conversion.
In Pycharm you can use the interface to deal with that, see this link. But I like the python -m because it works from anywhere, not only inside Pycharm.
make a class inside a conversion.py, then you can import it from test_conversion.py.
conversion.py
class convert():
def km_to_mm(input):
output = input * 1000000
return output
then import it in test_conversion.py
input = 0.001 # specify your input value
from conversion import convert
converted = convert().a_to_b(input)
converted will have value 1000
make sure you use the same folder. otherwise should use folder name to import. like
import foldername.conversion
from foldername.conversion import convert
I really appreciate all of you who tried to help. I got the problem solved in a very ridiculous manner, below is to someone who might face the same issue in the future.
So, in case you see the next:
You use PyCharm (no idea how other IDEs behave)
You created a module & want to import it into other files of your project
You type import module_name
While you type it, the string looks active and autocomplete even proposes you your module name but as only you finished typing, the import string turns grey, PyCharm throws you a warning saying Unused import statement, yellow bulb next to the import string suggests you delete the import string
--> This does not mean you are not able to import your module, it means you've done it and now can call anything from your module in the code below.
This taught me to pay some more time to read docs before jumping to using anything new and think better about UX in anything I do.

How to structure a python package made of multiple sub-projects?

what is the correct way to structure a python package for multiple functionalities?
I have a case where my project can be broken into 3 completely separate, logical chunks that are entirely decoupled. As such, I have broken the sub-projects into respective folders in my python package project. This has led to a file structure like this;
foobartoo
|_setup.py
|_foobartoo
|_foo
| |_foo.py
|
|_bar
| |_bar.py
|
|_too
|_too.py
This method keeps things together but separate nicely, but after installing I have noticed a definite problem. The only way to access one of the files is to do
from foobartoo.foo.foo import <method/class>
or
import foobartoo.foo.foo as <something>
<something>.<method/class>
This seems extremely impractical.
I can see two alternatives
scrapping the folder system and having foo.py, bar.py and too.py in the same directory under foobartoo.
This seems bad because it will be impossible to know which file/code belongs to which of the projects
breaking the single package into multiple packages.
This seems ok, but it would be better if the 3 things were together as they are intended to be used together.
I have looked at numpy and its source code, and somehow they seem to have a lot of their functionality behind many folders, yet they dont need to reference any of the folders. This seems ideal, just being able to do something like
import foobartoo
foobartoo.<classname/methodname>()
You can add additional paths to your 'main' script so sys will search automatically to many directories like:
import sys
sys.path.append(example_sub_dir)
import sub_script
sys.path.remove(example_sub_dir)
!note that you can add many directories on sys to search but you need to care as import time will be rise respectivelly

Proper way to import across Python package

Let's say I have a couple of Python packages.
/package_name
__init__.py
/dohickey
__init__.py
stuff.py
other_stuff.py
shiny_stuff.py
/thingamabob
__init__.py
cog_master.py
round_cogs.py
teethless_cogs.py
/utilities
__init__.py
important.py
super_critical_top_secret_cog_blueprints.py
What's the best way to utilize the utilites package? Say shiny_stuff.py needs to import important.py, what's the best way to go about that?
Currently I'm thinking
from .utilities import important
But is that the best way? Would it make more sense to add utilities to the path and import it that way?
import sys
sys.path.append(os.path.basename(os.path.basename(__file__)))
import utilities.super_critical_top_secret_cog_blueprints
That seems clunky to add to each of my files.
I think the safest way is always to use absolute import, so in you case:
from package_name.utilities import important
This way you won't have to change your code if you decide to move your shiny_stuff.py in some other package (assuming that package_name will still be in your sys.path).
According to Nick Coghlan (who is a Python core developer):
"“Never add a package directory, or any directory inside a package, directly to the Python path.” (Under the heading "The double import trap")
Adding the package directory to the path gives two separate ways for the module to be referred to. The link above is an excellent blog post about the Python import system. Adding it to the path directly means you can potentially have two copies of a single module, which you don't want. Your relative import from .utilities import important is fine, and an absolute import import package_name.utilities.important is also fine.
A "best" out-of-context choice probably doesn't exist, but you can have some criteria choosing which is better for your use cases, and for such a judgment one should know are the different possible approaches and their characteristics. Probably the best source of information is the PEP 328 itself, which contains some rationale about declaring distinct possibilities for that.
A common approach is to use the "absolute import", in your case it would be something like:
from package_name.utilities import important
This way, you can make this file it a script. It is somewhat independent from other modules and packages, fixed mainly by its location. If you have a package structure and need to change one single module from its location, having absolute path would help this single file to be kept unchanged, but all the ones which uses this module it should change. Of course you can also import the __init__.py files as:
from package_name import utilities
And these imports have the same characteristics. Be careful that utilities.important try to find a variable important within __init__.py, not from important.py, so having a "import important" __init__.py would help avoiding a mistake due to the distinction between file structure and namespace structure.
Another way to do that is the relative approach, by using:
from ..utilities import important
The first dot (from .stuff import ___ or from . import ___) says "the module in this [sub]package", or __init__.py when there's only the dot. From the second dot we are talking about parent directories. Generally, starting with dots in any import isn't allowed in a script/executable, but you can read about explicit relative imports (PEP 366) if you care about scripts with relative imports.
A justification for relative import can be found on the PEP 328 itself:
With the shift to absolute imports, the question arose whether relative imports should be allowed at all. Several use cases were presented, the most important of which is being able to rearrange the structure of large packages without having to edit sub-packages. In addition, a module inside a package can't easily import itself without relative imports.
Either case, the modules are tied to the subpackages in the sense that package_name is imported first no matter which the user tried to import first, unless you use sys.path to search for subpackages as packages (i.e., use the package root inside sys.path)...but that sounds weird, why would one do that?
The __init__.py can auto-import module names, for that one should care about its namespace contents. For example, say important.py has an object called top_secret, which is a dictionary. To find it from anywhere you would need
from package_name.utilities.important import top_secret
Perhaps you want be less specific:
from package_name.utilities import top_secret
That would be done with an __init__.py with the following line inside it:
from .important import top_secret
That's perhaps mixing the relative and absolute imports, but for a __init__.py you probably know that subpackage makes sense as a subpackage, i.e., as an abstraction by itself. If it's just a bunch of files located in the same place with the need for a explicit module name, probably the __init__.py would be empty (or almost empty). But for avoiding explicit module names for the user, the same idea can be done on the root __init__.py, with
from .utilities import top_secret
Completely indirect, but the namespace gets flat this way while the files are nested for some internal organization. For example, the wx package (wxPython) do that: everything can be found from wx import ___ directly.
You can also use some metaprogramming for finding the contents if you want to follow this approach, for example, using __all__ to detect all names a module have, or looking for the file location to know which modules/subpackages are available there to import. However, some simpler code completion utilities might get lost when doing that.
For some contexts you might have other kind of constraints. For example, macropy makes some "magic" with imports and doesn't work on the file you call as a script, so you'll need at least 2 modules just to use this package.
Anyhow, you should always ask whether nesting into subpackages is really needed for you code or API organization. The PEP 20 tells us that "Flat is better than nested", which isn't a law but a point-of-view that suggests you should keep a flat package structure unless nesting is needed for some reason. Likewise, you don't need a module for each class nor anything alike.
Use absolute import in case you need to move to a different location.

Using and Installing Django Custom Field Models

I found a custom field model (JSONField) that I would like to integrate into my Django project.
Where do I actually put the JSONField.py file? -- Would it reside in my Django project or would I put it in something like: /django/db/models/fields/
Since I assume it can be done multiple ways, would it then impact how JSONField (or any custom field for that matter) would get imported into my models.py file as well?
It's worth remembering that Django is just Python, and so the same rules apply to Django customisations as they would for any other random Python library you might download. To use a bit of code, it has to be in a module somewhere on your Pythonpath, and then you can just to from foo import x.
I sometimes have a lib directory within my Django project structure, and put into it all the various things I might need to import. In this case I might put the JSONField code into a module called fields, as I might have other customised fields.
Since I know my project is already on the Pythonpath, I can just do from lib.fields import JSONField, then I can just do myfield = JSONField(options) in the model definition.
For the first question, I would rather not put it into django directory, because in case of upgrades you may end up loosing all of your changes. It is a general point: modifying an external piece of code will lead to increased maintenance costs.
Therefore, I would suggest you putting it into some place accessible from your pythonpath - it could be a module in your project, or directly inside the site-packages directory.
As about the second question, just "installing" it will not impact your existing models.
You have to explicitly use it, by either by adding it to all of your models that need it, either by defining a model that uses it, and from whom all of your models will inherit.
The best thing would be to keep Django and customizations apart. You could place the file anywhere on your pythonpath really

Why does mass importing not work but importing definition individually works?

So I just met a strange so-called bug. Because this work on my other .py files, but just on this file it suddenly stopped working.
from tuttobelo.management.models import *
The above used to work, but it stopped working all of a sudden, and I had to replace it with the bottom.
from tuttobelo.management.models import Preferences, ProductVariant, UserSeller, ProductOwner, ProductModel, ProductVariant
from tuttobelo.management.models import ProductMeta, ShippingMethods
I know the following is the better way of coding, however ALL of the models mentioned in models are used, so my question is, what possible reasons can wildcard stop working?
The error I got was that the model I was trying to import does not exist, only if I remove the wildcard and import the name of the model could I get it imported properly.
Thanks!
Maybe the models module has an __all__ which does not include what you're looking for. Anyway, from ... import * is never a good idea in production code -- we always meant the import * feature for interactive exploratory use, not production use. Specifically import the module you need -- use that name to qualify names that belong there -- and you'll be vastly happier in the long run!-)
There are some cases in Python where importing with * will not yield anything. In your example, if tuttobelo.management.models is a package (i.e. a directory with an __init__.py) with the files Preferences.py, ProductVariant.py, etc in it, importing with star will not work, unless you already have imported it explicitly somewhere else.
This can be solved by putting in the __init__.py:
__all__ = ['Preferences', 'ProductVariant', 'UserSeller', <etc...> ]
This will make it possible to do import * again, but as noted, that's a horrible coding style for several reasons. One, tools like pyflakes and pylint, and code introspection in your editor, stops working. Secondly, you end up putting a lot of names in the local namespace, which in your code you don't know where they come from, and secondly you can get clashes in names like this.
A better way is to do
from tuttobelo.management import models
And then refer to the other things by models.Preferences, models.ProductVariant etc. This however will not work with the __all__ variable. Instead you need to import the modules from the __init__.py:
import Preferences, ProductVariant, UserSeller, ProductOwner, <etc...>
The drawback of this is that all modules get imported even if you don't use them, which means it will take more memory.

Categories