Python3 relative imports - python

I'm tired of reading one-off use cases of relative imports so I figured I'd as a question to get an example of how to do a relative import from a directory above and bellow, for both importing module functions and class objects.
directory structure:
.
├── lib
│   ├── __init__.py
│   └── bar.py
└── src
├── main.py
└── srclib
├── __init__.py
└── foo.py
bar.py
def BarFunc():
print("src Bar function")
class BarClass():
def __inti__(self):
print("src Bar Class")
def test(self):
print("BarClass working")
foo.py
def FooFunction():
print("srclib Foo function")
class FooClass():
def __init__(self):
print("srclib Foo Class")
def test(self):
print("FooClass working")
Question: What is the syntax in python 3 to import for these use cases?
main.py
# What is the syntax to import in python 3?
# I want to be able to call FooFunc()
foo.FooFunc()
# I want to be able to create a FooClass() object
foo_class = foo.FooClass()
foo_class.test()
# I want to be able to call FooFunc()
bar.BarFunc()
# I want to be able to create a BarClass() object
bar_class = bar.BarClass()
bar_class.test()

It all depends on where you start your python interpreter from. In your case, I would suggest you to start the interpreter from your project's root directory while making the following changes:
In file src/srclib/__init__.py add:
from . import foo
The reason for doing this is to explicitly state in your __init__.py file what to import from your module.
In your main.py file, add the following:
from lib import bar
from src.srclib import foo
Hope this helps!

Related

How do I specify inheritence behavior on initialization of an object in Python?

I'm trying to write a class with a method that behaves differently based on data given on the initialization of the object. I want to pass this object code to run stored in a different file. The behavior should be as follows
foo = Myclass(config="return_a.py")
bar = Myclass(config="return_b.py")
foo.baz() # returns a
bar.baz() # returns b
# where return_a.py and return_b.py are files in the directory
The closest I've come to fixing it so far is using exec and having my configured python write to a file which I then read from. I don't know how I'd do this in memory
You can use importlib to import the files dynamically.
Let's say your project has the structure:
.
├── main.py
├── return_a.py
└── return_b.py
you can put in main.py your code:
import importlib
class Myclass:
def __init__(self, config) -> None:
config = importlib.import_module(
config)
self.baz = config.baz
foo = Myclass(config="return_a")
bar = Myclass(config="return_b")
foo.baz()
bar.baz()
This is assuming you have the function baz your return_a and return_b files. For example:
#return_a.py
def baz():
print("I am A")
#return_b.py
def baz():
print("I am B")
Now if you execute main.py you will get:
I am A
I am B

monkeypatch a function that needs to be imported in conftest

I'm trying to use pytest.monkeypatch to patch a function I've defined in another file. I then need to patch a function from another that relies on this first monkeypatch. Here's a simple example
# class_def.py
class C:
def __init__(self):
# Normally, there is something that makes self.p
# that will use a file that will exist on production
raise FileNotFoundError
def factory():
print('in factory')
return C()
----
# function_def.py
from .class_def import factory
foo = factory()
def bar():
return 0
----
# conftest.py
from unittest.mock import MagicMock
import pytest
import playground.class_def
#pytest.fixture(autouse=True)
def patch_c(monkeypatch):
fake_c = MagicMock()
def factory():
print('in monkey factory')
return fake_c
monkeypatch.setattr('playground.class_def.factory', factory)
from .function_def import bar
# Then I would patch bar
And running pytest . will fail with FileNotFoundError. I believe this happens because I am calling foo = factory() at the top level of function_def.py. I expected this not to happen because I am patching factory before doing this import, but that doesn't seem to be happening. Is there a way to ensure this monkeypatch.setattr will go into effect before from .function_def import bar in conftest.py?
Also, the file structure looks like
playground
|--- __init__.py
|--- conftest.py
|--- class_def.py
|--- function_def
You have direct access to the attribute you want to change. You don't need monkeypatch at all.
Here's my tree :
$ tree .
.
├── a.py
├── b.py
├── __init__.py
└── test_a.py
0 directories, 4 files
a.py
class A:
def __init__(self):
raise Exception
def factory():
return A()
b.py
import a
print(a.factory())
test_a.py
import a
def test_a():
def fake_factory():
return 'A'
a.factory = fake_factory
import b
And it works:
$ pytest
=============================================================================================== test session starts ===============================================================================================
platform linux -- Python 3.6.5, pytest-3.5.1, py-1.5.3, pluggy-0.6.0
rootdir: /home/ahorgnies/test/monkeypatch, inifile:
plugins: remotedata-0.2.1, openfiles-0.3.0, doctestplus-0.1.3, arraydiff-0.2
collected 1 item
test_a.py . [100%]
============================================================================================ 1 passed in 0.01 seconds =============================================================================================

Wrong Icon on Kivy Window in Windows

I have a Kivy application that otherwise works fine. In my App class, I have the following...
class OmissionApp(App):
"""
Application-level class, builds the application
"""
def __init__(self, **kwargs):
"""
Initialize a new OmissionWindow.
"""
super().__init__(**kwargs)
# Some other init stuff
def build_config(self, config):
"""
Configure the application.
"""
# Icon
Config.set('kivy', 'window_icon', 'resources/icons/omission_icon.png')
# And more stuff...
def build(self):
"""
This function starts the application by constructing
it from widgets and properties.
"""
# Set the title and icon.
self.title = "Omission"
self.icon = "resources/icons/omission_icon.png"
# Create the window.
omission_app = OmissionWindow()
# And more...
# Return the application.
return omission_app
So, this works just fine on Ubuntu (16.04, 17.10), but on Microsoft Windows (7), it isn't showing the icon at all.
My file tree looks roughly like this (I left some irrelevant stuff out)...
.
├── omission/
│ ├── data/
│ ├── game/
│ ├── interface/
│ ├── resources/
│ │ ├── audio/
│ │ ├── content/
│ │ ├── font/
│ │ └── icons/
│ │ ├── omission_icon.ico
│ │ └── omission_icon.png
│ ├── __init__.py
│ ├── __main__.py
│ └── run.py
└── omission.spec
I've tried several ways of doing this (although the documentation indicates the code itself is right.) Also, again, it works on Ubuntu. I've tried using an *.ico icon file instead of the *.png, but no luck.
How do I get the icon working on Microsoft Windows?
The example given in the documentation works because it's just a filename - the actual icon file exists in the same folder as the application.
Moving the icon to the application root, and then changing the code to the following, does work on any system.
self.icon = "omission_icon.png"
But, since I have my icon in a subdirectory, I now am dealing with a filepath. It's really easy to forget that paths on UNIX and Windows are not the same. Hardcoded paths, simply put, are not portable.
Thus, I need to fix this by creating a portable system path, via os.path.
import os.path
# ...
Config.set('kivy', 'window_icon', os.path.join("omission", "resources", "icons", "omission_icon.png"))
# ...
self.icon = os.path.join("omission", "resources", "icons", "omission_icon.png")
The os.path starts from the current directory by default. In this case, that happens to be the parent of the application's directory.
NOTE: Technically, you can leave the "omission" directory off, but it won't work after you've packaged with PyInstaller. The way above works either way.
Now our icon works on any system!
NOTE: Yes, I know this may seem bright-blazingly obvious, but I walked in circles long enough before spotting the obvious that it seemed worthy of a Q&A.

Package python codes which uses __import__ function

My directory structure is:
./
├── foo
│   ├── bar.py
│   ├── foo.py
│   └── __init__.py
└── main.py
with:
bar.py:
def get_data():
return 'ha'
foo.py:
class foo:
def __init__(self):
self.lib = __import__('bar', fromlist=['bar'])
self.data = self.lib.get_data()
def print_data(self):
print(self.data)
if __name__=='__main__':
f = foo()
f.print_data()
__init__.py:
from foo import foo
and main.py:
from foo import foo
a = foo()
a.print_data()
Running python foo.py I get ha correctly, but running python main.py I get the following message:
Traceback (most recent call last):
File "main.py", line 3, in <module>
a = foo()
File ".../foo/foo.py", line 3, in __init__
self.lib = __import__('bar')
ImportError: No module named bar
My requirements are 1) making foo to work like a package, 2) using __import__ in foo.py's __init__ function instead of import in the first line of foo.py.
I changed line 3 of foo.py to self.lib = __import__('foo.bar', fromlist=['bar']), and then got the correct answer. But that is not I want, since running python foo.py will lead to a failure and that is not a package solution when the whole directory ./ become another package. It seems an import path problem that I cannot figure out.
Changing foo.py to that makes it work correctly:
import importlib
class foo:
def __init__(self):
self.lib = importlib.import_module('foo.bar')
self.data = self.lib.get_data()
def print_data(self):
print(self.data)
if __name__=='__main__':
f = foo()
f.print_data()

Calling class in another folder

Given the folder structure as follows:
folder1
__init.py__
python1.py
folder2
__init.py__
class1.py
say in class1.py I have
class aa:
def __init__(self,max):
self.max=max
def hello(self):
print(self.max)
What should I import from python1.py to invoke aa.hello()?
i.e., in python1.py
#what do I need to import here
myaa=aa(100)
myaa.hello()
Answer my own:
from folder2.class1 import aa

Categories

Resources