0

So basicly I want to acces a created module from a folder on the parent directory from the folder I am

Currently I'm at Twitter.py and I want to access /utils/magic_eden.py

On the __init__.py file I've got:

from .magic_eden import MagicEden
from .open_sea import OpenSea
from .tools import Tools

Now inside the Twitter.py file im trying to import these classes of the module by doing:

from utils import MagicEden

But im getting ModuleNotFoundError: No module named 'utils'.

I've tried so many sort of things but none worked. What should I do?

(btw if I execute the __init__.py file I get ImportError: attempted relative import with no known parent package)

enter image description here

Oscar
  • 41
  • 3
  • 6

2 Answers2

1

From what I see, it seems that utils is a utility package logically separate from the code in Twitter.py. Given this, what you want is to cause your utils package to be on your Python search path (sys.path) so that you can import that package as a separate entity (no relative paths). If you don't want to configure your environment to place utils on the Python search path, you can do it in your Twitter.py file. Here's how that looks:

import os
import sys

here = os.path.dirname(__file__)

sys.path.append(os.path.join(here, '..'))

import utils

utils.MagicEden.hello()
utils.OpenSea.hello()
utils.Tools.hello()

The first line of code computes the full path to the directory containing Twitter.py. The second line then computes the path to the parent directory of that directory and adds that path to sys.path, the Python search path. After doing this, import utils will work, giving you access to that package and everything imported in that package's __init__.py file.

I created three small files for magic_eden.py, open_sea.py, and tools.py, each containing something that looks like this:

class MagicEden:

    @staticmethod
    def hello():
        print("Hello from MagicEden!")

I can then run Twitter.py, and I get the following result with no additional configuration:

Hello from MagicEden!
Hello from OpenSea!
Hello from Tools!

There's nothing wrong with using the above solution during early development. But you will likely at some point want to remove the code that is hacking sys.path and instead install your module in a more official way. There's a way to do this from the start so that you never have to change code on either side when you want to install your module in the official way...

What I do in situations like this is create a setup.py file in my package that lets me build it as an installable package. There are many docs and tutorials on the net that explain how to do this. Here's just one of them: https://python-packaging-tutorial.readthedocs.io/en/latest/setup_py.html

Once you've done this, what you can do is install your package in "development mode" (pip install -e <package file>). What this does is install the package so that your system can find it (adds it to sys.path as a package), but installs links to the original sources rather than installing copies of them. In this mode, when you make changes to that package's source files, the changes take immediate effect in the installed module. So now you have the best of both worlds. Your package is installed in the official way such that you don't need to do anything special in the code that imports that package (no need to modify sys.path), and yet you can still make changes directly to the sources for that package and not have to keep reinstalling the package to see those changes take affect.

When you're done messing with a package in this way, you can reinstall it in the regular mode (pip install <package file>) and isolate changes to the sources for that package from uses of the package elsewhere on your system. At this point, you need to rebuild and reinstall the package to see any changes you've made to its sources.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44
  • I tried this solution and it worked fine but now I'm getting an error at magic_eden.py file because I'm trying to import one class from tools like this: from tools import Constants, but it throws an ModuleNotFoundError: No module named 'tools' error. If you can check this github repo, this guy is importing from sibling folders and he is not using and sys.path method or anything, for example from "logger" he imports Logger, being at "profile_parser.py" at "controller" folder, any idea how he is doing it? https://github.com/iSnkh/SyneziaRaffles/tree/master/srcs – Oscar Feb 15 '22 at 23:37
  • @Oscar - I gotta run for now. I can help you with this a bit later on if you're still struggling. I've spent a lot of time building and deploying Python modules. I'm pretty sure I could help you figure out the right way to do what you want to do. Best of luck in the meantime! – CryptoFool Feb 15 '22 at 23:38
  • No hurries :) Will be waiting for your answer and will let you know if I fix the issue. Thank you ! – Oscar Feb 15 '22 at 23:41
  • @Oscar - Hey. I'm back. Without trying it myself, what I think you want is `from utils.tools import Constants`. Remember that `utils` is the package that is on the search path. `tools` is not directly on the search path. You have to go through `utils` to get to it. – CryptoFool Feb 16 '22 at 00:03
  • @Oscar - looking at the example you mention in your comment, what I'm suggesting here is exactly the same case. Notice that in this other package, the import statement is `from logger.logger import Logger`. Here, the first `logger` is your `utils` and the second one is your `tools`. That the two names are the same is only because there is both a directory named `logger` and a file named `logger.py` in that package. – CryptoFool Feb 16 '22 at 00:12
  • yeah that's exactly how I have the code, just like the one from github, but for some reason, even with `__init__.py` created, i don't know why keeps throwing ModuleNotFoundError: No module named 'utils'. It is like if python wasn't recognizing utils as a module, even with creating the `__init__.py` file. – Oscar Feb 16 '22 at 00:39
  • @Oscar - so the first case, `import utils` (which is doing `from .tools import Tools` under the hood), works but `from utils.tools import Constants` does not? That's really strange. Since it didn't work for you, I tried this. It works fine for me. I'm running standard Python 3.9 on a Mac, with my file structure looking identical to yours (without the Tesseract-OCR dir). Maybe it's a Windows thing? – CryptoFool Feb 16 '22 at 01:07
  • Is there any way I can contact you? Maybe via discord or something. I've spent the whole day trying to solve this but with no success. I've tried on another Windows pc too and it has the same problem. I will leave my discord here if you want to hit me up :). banjo#1917 – Oscar Feb 17 '22 at 02:13
  • @Oscar - happy to help if you're still struggling - I just sent you a friend request on Discord. The only problem I will have in helping you is that I don't do Windows ;) – CryptoFool Feb 17 '22 at 18:10
0

You're attempting to import from a "sibling" folder.

To do this, you'll need to add __init__.py to your Twitter/ and parent src/ folders.

Then you'll need to import the path as Twitter.py doesn't know about any parent structure in this setup. You can then import from the utils module which is in the path.

import sys

sys.path.append('..')

from utils.magic_eden import MagicEden

assuming MagicEden is a class in your magic_eden.py file.

tdpu
  • 400
  • 3
  • 12
  • Still having the same issue – Oscar Feb 15 '22 at 22:20
  • whoops, I'm afraid I didn't look at your directory structure carefully enough. You'll need to use `sys.path.append` to do what you're trying (import from a "sibling" folder). Updating the answer. – tdpu Feb 15 '22 at 22:33
  • still not working :( I've searched on google and did this exact same example here: https://www.geeksforgeeks.org/python-import-from-sibling-directory/ and I'm still getting this same error ModuleNotFoundError: No module named 'fldr2'. – Oscar Feb 15 '22 at 23:28
  • Adding `..` to `sys.path` is absolutely NOT what you want to do. If you do this, Python's package search behavior becomes dependent on what the current working directory (CWD) happens to be at the moment. It's bad enough to use the notion of the CWD to compute an absolute path and then add that to `sys.path`. Adding a relative path directly to `sys.path` is really bad. Now the behavior of all of your `import` statements will change whenever your CWD happens to change. If you forget that you've done this, the behavior of your system can one day drive you absolutely crazy. – CryptoFool Feb 15 '22 at 23:28
  • @Oscar - did you try my solution? It's the same idea, but it uses an absolutely foolproof way of computing the path that you want to add to your Python search path (`sys.path`). That method will not break in the face of some external configuration change, like a change to your CWD. It makes good sense that the method shown here might not work for you. It is dependent on what your CWD happens to be, which there's no way to even know much less make sure never changes out from under you. – CryptoFool Feb 15 '22 at 23:30
  • @CryptoFool definitely a more failsafe approach, although best would be to use a proper `setup.py` and create a true package. Then you can import utils from anywhere without mucking around with relative imports. I provided a straightforward solution assuming they were running `twitter.py` as a "script". – tdpu Feb 16 '22 at 04:47
  • @Oscar what is `fldr2`? It seems that at least you've solved the fact that it can't import `utils`. Just need to carefully identify which package is missing and see if you're missing an import (i.e. in your `__init__.py` somewhere)? – tdpu Feb 16 '22 at 04:50