Python import library in submodule - python

I have the following structure:
run.py
app/hdfs_lib_test.py
app/src/HDFSFileReader.py
This is a Flask app.
The HSFDFileReader.py contains:
class HDFSFileReader:
"""This class represents a reader for accessing files in a HDFS."""
def __init__(self):
pass
#classmethod
def read(cls, file_path):
lines = []
try:
client = Config().get_client('dev')
with client.read('Serien_de', encoding='utf-8', delimiter='\n') as reader:
for line in reader:
lines.append(line)
except:
print("ERROR: Could not read from HDFS.")
raise
return lines
When I run ./run.py I get the ImportError: No module named 'hdfs'. However the library is installed and I can call python hdfs_lib_test.py, which contains the following:
from hdfs import Config
try:
client = Config().get_client('dev')
with client.read('Serien_de',encoding='utf-8',delimiter='\n') as reader:
for line in reader:
print(line)
except:
raise
So to me it seems, that because HDFSFileReader is part of a submodule and hdfs_lib_test.py isn't. The former doesn't work, but the later does. But how do I fix the import problem?
I can't be a circular import issue, I searched the whole project for imports of hdfs and the hdfs_lib_test.py is not used by the actual project.

Related

Python behave fixture on feature level not loaded

This is somewhat related to this question, but I have some further problems in this minimal example below.
For a feature test I prepared a fixture which backs up a file which shall be modified during the test run (e.g. a line is appended). After the test run this fixture restores the original file.
Project Files:
└───features
│ environment.py
│ modify_file.feature
│
└───steps
file_ops.py
#!/usr/bin/env python
# FILE: features/environment.py
import logging
from behave import fixture
from behave.runner import Context
logger = logging.getLogger(__name__)
#fixture
def backup_file(context: Context):
"""
A file will be modified during the feature test.
This fixture shall backup the file before the feature test
and restore the backup after the test.
"""
file = Path.home() / "important.txt"
backup_suffix = ".backup"
file.touch()
file.replace(file.with_suffix(backup_suffix))
logger.info("File backed up")
yield
file.with_suffix(backup_suffix).replace(file)
logger.info("File restored")
# FILE: features/modify_file.feature
#fixture.backup.file
Feature: Modify file
#wip
Scenario: Append a line to a file
Given the file exists
When I append a line to the file
Then the line appears at the end of the file
#!/usr/bin/env python
# File features/steps/file_ops.py
from pathlib import Path
from behave import given
from behave import when
from behave import then
from behave.runner import Context
import logging
logger = logging.getLogger(__name__)
file = Path.home() / "important.txt"
#given("the file exists")
def step_impl(context: Context):
logger.info(f"Touching file")
file.touch()
#when("I append a line to the file")
def step_impl(context: Context):
logger.info(f"Appending a line to file")
context.appended = "Test line appended\n"
with open(file, mode="a") as f:
f.write(context.appended)
#then("the line appears at the end of the file")
def step_impl(context: Context):
logger.info(f"Checking if line was appended")
with open(file, mode="r") as f:
for line in f:
pass
logger.info(f"Last line is '{line.strip()}'")
assert line == context.appended
I want to apply the fixture at the feature level before all scenarios are run. The file shall be restored after all scenarios have run. However, this is apparently not the case.
When I run behave -w (no log capture, wip tags only), I don't see any log lines from the fixture being output and also with every run I see another line appended to the file. This means the file is not being backed up and restored. The fixture is not applied even if in the I move the fixture down to the Scenario level modify_file.feature file.
Can you help me understand what is going on here? I'm also curious why the fixture tag is used with dot notation (#fixture.backup.file) rather than (fixture.backup_file) as this would be similar to the actual function name. There is no explanation of this in the behave documentation.
I also had trouble setting up a fixture because the docs aren't super clear that you have to explicitly enable them. It isn't enough to have the #fixture decoration in features/environment.py. You also have to call use_fixture(). For example, inside before_tag(), like this:
def before_tag(context, tag):
if tag == "fixture.backup.file":
use_fixture(backup_file, context)
This example helped me figure that out:
https://github.com/behave/behave/blob/main/features/fixture.feature#L16
Here is how you can do it without #fixture, just by using the before_tag hook:
#!/usr/bin/env python
# FILE: features/environment.py
import logging
from behave.runner import Context
from behave.model import Tag
from pathlib import Path
logger = logging.getLogger(__name__)
def rename_file(origin: Path, target: Path) -> tuple[Path, Path]:
"""
Will rename the `origin` to `target`, replaces existing `target`
"""
origin.touch()
backup = origin.replace(target)
logger.info(f"File {str(origin)} renamed to {str(target)}")
return origin, backup
def before_tag(context: Context, tag: Tag):
if tag == "backup.file.important.txt":
file = Path.home() / "important.txt"
backup = file.with_suffix(".backup")
context.file, context.backup_file = rename_file(file, backup)
context.add_cleanup(rename_file, context.backup_file, context.file)
#!/usr/bin/env python
# File features/steps/file_ops.py
from pathlib import Path
from behave import given
from behave import when
from behave import then
from behave.runner import Context
import logging
logger = logging.getLogger(__name__)
#given("the file exists")
def step_impl(context: Context):
logger.info(f"Touching file")
context.file.touch()
#when("I append a line to the file")
def step_impl(context: Context):
logger.info(f"Appending a line to file")
context.appended = "Test line appended\n"
with open(context.file, mode="a") as f:
f.write(context.appended)
#then("the line appears at the end of the file")
def step_impl(context: Context):
logger.info(f"Checking if line was appended")
with open(context.file, mode="r") as f:
for n, line in enumerate(f, start=1):
logger.info(f"Line {n} is {line}")
logger.info(f"Last line is '{line.strip()}'")
assert line == context.appended
But using a fixture and the fixture registry as described in the Realistic example in behave's documentation is superior, as you will likely end up having many more setup-cleanup pairs later on.

Unpickling a class raises attribute error

I wrote a class A in the Class_A.py file
class A():
def square(self, num):
return num*num
Next in pickle_A.py, I saved the class in a file 'class_A_imported_from_class_A'
import pickle
from Class_A import A
if __name__ == '__main__':
file_name = 'class_A_imported_from_class_A'
with open(file_name,'wb') as f:
pickle.dump(A,f,pickle.HIGHEST_PROTOCOL)
Then I moved the file to another location, where I ran Unpickling_imported_class.py to unpickle this file.
import pickle
file_name = 'class_A_imported_from_class_A'
with open(file_name,'rb') as f:
B = pickle.load(f)
So, I get the error:
B = pickle.load(f)
builtins.ModuleNotFoundError: No module named 'Class_A'
Now, I know that the error will go, if I copied the Class_A into this folder. The constraint is that I cannot.
I was able to do this using cloudpickle, but in that, I have to pickle the file also using cloudpickle.
My work demands that I should be able to unpickle classes directly, i.e. if there's a pickled file that has the data for a class, I should be able to read a class directly. Is there a way that I can do it?

Calling a variable from another function inside another file

I'm trying to import a dictionary variable from a function inside A.py, to my new python file B.py. However, it doesn't seem to bring it along. codedict is the variable I'm importing.
Python file A.py
import csv
def load(filename):
with open(filename, 'r') as datafile:
reader = csv.reader(datafile)
next(reader)
global codedict
codedict = sorted({k[1].lower() for k in reader})
load("Dataforcars.cvs")
In my new python file, I'm trying to bring along the dictionary.
Python file B.py
import csv
from A import load
print(codedict)
However, "codedict" is not defined in B.py.
I think this will helpful:
A.py:
import csv
def load(filename):
with open(filename, 'r') as datafile:
reader = csv.reader(datafile)
next(reader)
return sorted({k[1].lower() for k in reader})
B.py:
import csv
from A import load
codedict = load("Dataforcars.cvs")
print(codedict)
Codedict is local variable, you should return it rather than try to access it directly.

Creating Python submodule

I want to create a tool called unifile for saving and opening files
like this unifile.open.yaml("file.yaml").
This is my structure:
unifile
|
├-open
| └--__init__.py
|
└-save
└--__init__.py
Code that call my module:
import unifile
a = unifile.open.yaml("file.yaml")
open/init.py
import yaml
class open():
def yml(self, file_path):
try:
with open(file_path, "r", encoding="utf-8") as yaml_conf:
yaml_file = yaml.safe_load(yaml_conf)
return yaml_file
except OSError:
print("Can't load yaml")
1 error if I import unifile always say:
module unifile has no atribute open
2 error in __init__.py I can't open file
[pylint] Context manager 'open' doesn't implement enter and exit. [not-context-manager]
here adding solution to ur problem, make your project structure like this.
add unifile/__init__.py file in the unifile itself not in other modules.
then unifile/open/_open.py file content
import yaml
class Open():
def __init__(self):
pass
def yml(self, file_path):
try:
with open(file_path, "r", encoding="utf-8") as yaml_conf:
yaml_file = yaml.safe_load(yaml_conf)
return yaml_file
except OSError:
print("Can't load yaml")
content of the unifile/__init__.py file
from .open._open import Open
in terminal run the program like this
Also, It is better to create a object element first then proceed ahead.
Two issues, two answers.
First, you should add an init file in unifile. With this, Python will understand that unifile is a package with a sub package.
Second, open is a built-in function and you overwrite it by calling your class open. Change your class name and it should work.
You are getting this error because unifile is not a package. There isn't any init.py file at the top level same as open and save. You also cannot call open.yml directly, because open is a class in package open, so either you will have to import open from open, create its instance and then call iml on that instance.
from open import open
a = open().yml('file.yml')
You are getting this error, because you are trying to override an existing keyword in Python open which you should strictly prohibit doing. So you should name your class anything except a reserved keyword.

Python - Executable Directory Import Issues

I am working through an online python course and have an issue that I am having trouble working through. I have a executable directory with the following layout
reader/
|--__main__.py
|--reader
|--__init__.py
|--reader.py
|--compressed
|--gzipped.py
|--bzipped.py
|--__init__.py
When I do not have these modules in the top directory, I can import just fine and use all of the functionality. When I put them in the top level directory and run the executable directory from the command line with
python reader test.gz
I am getting the following error
AttributeError: module 'reader' has not attribute 'Reader'
The code for main.py is
import sys
import reader
r = reader.Reader(sys.argv[1])
try:
print(r.read())
finally:
r.close()
The code for reader.py is
import os
from reader.compressed import gzipped, bzipped
extension_map = {
'.bz2': bzipped.opener,
'.gz': gzipped.opener,
}
class Reader:
def __init__(self, filename):
extension = os.path.splitext(filename)[1]
opener = extension_map.get(extension, open)
self.f = opener(filename, 'rt')
def close(self):
self.f.close()
def read(self):
return self.f.read()
I can provide the rest of the files if needed. I am using the current distribution of Anaconda. Any help or explanations would be appreciated.
It seems to me that because of your __init__.py file the folder reader is seen as a python module. Thereore your main.py code tries to find the class Reader at the folder level, but you need to look for Reader within reader.py. Can you try changing
r = reader.Reader(sys.argv[1])
to
r = reader.reader.Reader(sys.argv[1])
Also, you have a lot of modules/files/modules called reader, e.g. the class Reader is in reader/reader/reader.py. This I would try to avoid as it can lead to confusion.
When you have multiple modules/modules that are named identically you can often get the namespace into a recursive loop. Also, the Reader class is capitalized.
Like mentioned above, try python reader.reader test.gz
While you are troubleshooting try the tab-complete feature to see if your modules are being loaded properly.

Categories