Local directory shadowing 3rd party package - python-3.x

I'm trying to figure out if this is an error in my design, or an error in the redis-py library. Essentially, my understanding of namespace in Python is that packages should be designed such that all components are under the package namespace namespace. Meaning, if I have a queue in packageA and a queue in packageB, there should be no collision since they are namespaced (packageA.queue and packageB.queue). However, I'm running into an error in a package I am building.
This is the directory structure for the package I am building:
○ → tree
.
├── __init__.py
├── net
│ ├── __init__.py
│ ├── rconn.py
└── test.py
The __init__.py files are all empty. Here's the code of my test.py file:
○ → cat test.py
from net import rconn
and here's the code from my net/rconn.py file:
○ → cat net/rconn.py
import redis
Running test.py, everything works, no errors. However, if I add a queue directory in here and create an empty init.py within, here's the new tree:
○ → tree
.
├── __init__.py
├── net
│ ├── __init__.py
│ ├── rconn.py
├── queue
│ ├── __init__.py
└── test.py
Running test.py results in the following error:
Traceback (most recent call last):
File "test.py", line 1, in <module>
from net.rconn import ass
File "/Users/yosoyunmaricon/python_test/net/rconn.py", line 1, in <module>
import redis
File "/usr/local/lib/python3.7/site-packages/redis/__init__.py", line 1, in <module>
from redis.client import Redis, StrictRedis
File "/usr/local/lib/python3.7/site-packages/redis/client.py", line 10, in <module>
from redis._compat import (b, basestring, bytes, imap, iteritems, iterkeys,
File "/usr/local/lib/python3.7/site-packages/redis/_compat.py", line 139, in <module>
from queue import Queue
ImportError: cannot import name 'Queue' from 'queue' (/Users/yosoyunmaricon/python_test/queue/__init__.py)
So, I get what's happening. The Redis code says from queue import Queue, and when I create an empty queue directory (i.e., no Queue), it breaks the package. My question is this: Is that good design? Should the Redis package be more explicit and say something along the lines of from redis.queue import Queue, or is this simply an error in my own design?

It's not the Redis package that should adjust here, because it cannot know or cannot handle the different ways users could integrate the Redis package into their own applications, like how you have a similarly named queue package. Furthermore, there is no redis.queue because that queue, is not part of redis, but the built-in Python queue package. You can go to /usr/local/lib/python3.7/site-packages/redis/_compat.py and print out the queue.__file__, which would give you the path to Python's queue. It expects importing the built-in queue package.
Unfortunately for you, when Python builds the module search paths for resolving imports, it builds it in the following order:
The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.
...which puts your own queue at the start of the list and that's what gets imported. So, yes, getting an ImportError because you shadowed the built-in queue is more of an error in your own design.
You could probably do some tricks here with sys.path or PYTHONPATH, but why bother when you can just rename your queue to something else. Or, what I usually do is to group my own packages into a parent folder, named after the acronym for the project ("abcdlibs") or some app identier or something like "mylibs":
.
├── __init__.py
├── mylibs
│ └── queue
│ ├── __init__.py
├── mynet
│ ├── __init__.py
│ └── rconn.py
└── test.py
That way, you could make it clear that mylibs.queue is different from queue.

Related

Unable to import flask app folder in unittest folder of an repo

I am facing issues in writing unit test for my flask app. Exact issue is that test files in unit-test directory is not able to import files from app folder.
My directory structure:
.
├── Dockerfile
├── README.md
├── app
│ ├── __init__.py
│ ├── api.py
│ └── wsgi.py
├── docker
│ ├── docker-compose.yml
│ └── start.sh
├── requirements.txt
└── unit-test
├── __init__.py
└── test_api.py
Code in unit-test/test_api.py:
import unittest
from app import api
Absolute import throws this error:
from app import api
ModuleNotFoundError: No module named 'app'
I have tried the following after checking a few resources of absolute and relative imports, none of them worked.
from .. import app
error:
from .. import app
ImportError: attempted relative import with no known parent package
I checked a few questions on SO and someone recommended having _init_.py file in the unit-test folder as well but I already have a blank init.py file there.
I have reviewed numerous blogs and youtube tutorials and absolute imports work for them easily.
Please advise how to fix this error and run unit tests.
import sys
sys.path.append('/path/to/the/required/folder')
import name_of_file
if not an inbuilt package, Python only searches in the current directory when you try to import, thus we have to add this path, so that it looks inside this particular folder as well. After adding that, you can simply do the import.

File hierarchy and local imports

I'm trying to figure out file hierarchy and local imports in python3. I've run into a problem with a file structure below:
└── project
├── run.py
└── scripts
├── __init__.py
├── misc.py
├── script1.py
└── script2.py
I use relative imports for all files in the "scripts" directory.
from misc import x, y, z
I use absolute imports in run.py.
from scripts.misc import a, b, c
from scripts.script1 import more_stuff
My goal is to have the python files independent and fully functional. However, when I attempt to execute run.py, I encounter an error.
from misc import x, y, z
ModuleNotFoundError: No module named 'misc'
I would expect relative paths to be relative to the original file and not adopt the path of the executed file. Can I fix this by modifying the imports or file structure?
It also appears I don't understand how __init__.py works. I want to re-use generic package names (like "scripts"). I had assumed that __init__.py files would be read immediately downstream relative to the executed file: if run.py is executed, only the scripts directory at the same level should be considered. I have found that a distant (unrelated?) "scripts" directory receives priority. Is this something that can be addressed with more careful absolute paths? Example below.
└── dir
└── project1
| ├── run.py
| └── scripts
| ├── __init__.py
| └── settings.py
└── subdir
└── project2
├── run.py
└── scripts
├── __init__.py
└── settings.py
Executing run.py from "project1" will attempt to import the "scripts" directory from project2.
cannot import name 'variable' from 'scripts.settings' (/Users/.../dir/subdir/project2/scripts/settings.py)
Removing __init__.py from project2/scripts no longer produces the error when executing run.py from "project1".
You are saying:
I use relative imports for all files in the "scripts" directory.
from misc import x, y, z
But this is not relative. For relative you need to have
from .misc import x, y, z
To understand why the unrelated scripts is taking precedence, look on your sys.path and verify if indeed it comes before your scripts package. I can assume the is some leftover for ide path manipulation.

Unable to import a module from a sub-package into another sub-package

I am unable to import module from a different package.
The module connection.py is in a package instance_connector and the module record_parameter.py is in a different package called instance_parameters.
Both of these packages are the sub-packages of a package called snowflake.
Here is the tree diagram of directory structure.
snowflake
├── __init__.py
├── instance_connector
│ ├── __init__.py
│ └── connection.py
└── instance_parameters
├── __init__.py
├── load_parameters.py
├── modals.py
└── record_parameters.py
I am trying to import a module connection.py into module record_parameter.py like this -
record_parameter.py
from snowfake.instance_connector.connection import SnowflakeConnector
When I run this file via terminal using command python record_parameter.py it returns me an error stated below -
Traceback (most recent call last):
File "record_parameters.py", line 3, in <module>
from snowflake.instance_connector.connection import SnowflakeConnector
ModuleNotFoundError: No module named 'snowflake.instance_connector'
Where am I going wrong?
Have you tried appending the path which leads to the file connection.py using the sys module in record_parameters.py?
import sys
sys.path.append(1, 'xyz/snowflake/instance_connector/connection.py')

Error when importing variable from different script from different directory

I was working on a project where I was training a neural network and displaying information using a GUI in Python. However, I have a constant problem with importing modules. This is the error I get when trying to run Visualizer.Main
Traceback (most recent call last):
File "/home/user/SplineTrajectoryGenerator/Visualizer/Main.py", line 3, in <module>
from Visualizer.Field import Field
File "/home/user/SplineTrajectoryGenerator/Visualizer/Field.py", line 3, in <module>
from Visualizer.Utils import loadImage
File "/home/user/SplineTrajectoryGenerator/Visualizer/Utils.py", line 3, in <module>
from NeuralNetworks.Pose import Pose2D
File "/home/user/SplineTrajectoryGenerator/NeuralNetworks/Pose.py", line 4, in <module>
from Visualizer.Utils import constraint
ImportError: cannot import name 'constraint'
However, the strange thing is that the variable exists and can be found in Utils.py in the Visualizer folder. I was told that using the _init__.py
allows a directory to be used as a module to import methods and classes from scripts in other directories. I'm still confused about why this error happens when the variable exists. For reference the tree of all the files look like this.
.
├── NeuralNetworks
│ ├── __init__.py
│ ├──Main.py
│ └──Pose.py
├──README.md
└── Visualizer
├── Assets
│ ├── Field.png
│ └── Robot.png
├──Field.py
├── __init__.py
├──Main.py
├──Robot.py
└──Utils.py
In case you need to see the scripts, I have attached the whole folder below with this link. Any help would be greatly appreciated.
You seem to have a cyclic dependency between Visualizer.Utils and NeuralNetworks.Pose`:
from Visualizer.Utils import loadImage
File "/home/user/SplineTrajectoryGenerator/Visualizer/Utils.py", line
3, in
from NeuralNetworks.Pose import Pose2D
File "/home/user/SplineTrajectoryGenerator/NeuralNetworks/Pose.py",
line 4, in
from Visualizer.Utils import constraint
Python does not support cyclic dependencies - and that's actually a GoodThing(tm) since those are design issues (two modules should not depend on each other). The canonical solution is to create a new module for objects (classes, functions, whatever) that are necessary to both modules (ie in your case create a distinct module for 'constraint').

How to import from a sibling directory in python3?

I have the following file structure:
bot
├── LICENSE.md
├── README.md
├── bot.py # <-- file that is executed from command line
├── plugins
│   ├── __init__.py
│   ├── debug.py
│   └── parsemessages.py
├── helpers
│   ├── __init__.py
│   ├── parse.py
│   └── greetings.py
└── commands
   ├── __init__.py
   └── search.py
bot.py, when executed from the command line, will load in everything in the plugins directory.
I want plugins/parsemessages.py to import parse from the helpers directory, so I do that:
# parsemessages.py
from ..helpers import parse
parse.execute("string to be parsed")
I run python3 bot.py from the command line.
I get the following error:
File "/home/bot/plugins/parsemessages.py", line 2, in <module>
from ..helpers import parse
ValueError: attempted relative import beyond top-level package
So I change two dots to one:
# parsemessages.py
from .helpers import parse
parse.execute("string to be parsed")
...but I get another error:
File "/home/bot/plugins/parsemessages.py", line 2, in <module>
from .helpers import parse
ImportError: No module named 'plugins.helpers'
How can I get this import to work?
It's worth noting that I'm not attempting to make a package here, this is just a normal script. That being said, I'm not willing to mess around with sys.path - I want this to be clean to use.
Additionally, I want parse to be imported as parse - so for the example above, I should be typing parse.execute() and not execute().
I found this post and this post, but they start with a file that's quite deep in the file structure (mine is right at the top). I also found this post, but it seems to be talking about a package rather than just a regular .py.
What's the solution here?
You could remove the dots, and it should work:
# parsemessages.py
from helpers import parse
parse.execute("string to be parsed")
That's probably your best solution if you really don't want to make it a package. You could also nest the entire project one directory deeper, and call it like python3 foo/bot.py.
Explanation:
When you're not working with an actual installed package and just importing stuff relative to your current working directory, everything in that directory is considered a top-level package. In your case, bot, plugins, helpers, and commands are all top-level packages/modules. Your current working directory itself is not a package.
So when you do ...
from ..helpers import parse
... helpers is considered a top-level package, because it's in your current working directory, and you're trying to import from one level higher than that (from your current working directory itself, which is not a package).
When you do ...
from .helpers import parse
... you're importing relative to plugins. So .helpers resolves to plugins.helpers.
When you do ...
from helpers import parse
... it finds helpers as a top-level package because it's in your current working directory.
If you want to execute your code from the root, my best answer to this is adding to the Path your root folder with os.getcwd().
Be sure your sibling folder has a init.py file.
import os
os.sys.path.insert(0, os.getcwd())
from sibling import module

Resources