I have a third-party module (cx_Oracle) that I'd like to import whose location is unknown from environment to environment. I am currently using pythons configparser so I thought it would be a neat trick to set the location of the module within the config parser, append that location to path, and then import the third-party module from there.
This worked all fine and dandy until I began to refactor my code and started to split out logic into their own class/methods:
class Database:
def __init__(self, config):
self.CONFIG=config
sys.path.append(self.CONFIG.cx_oracle_path)
from cx_Oracle import cx_Oracle
self.open()
def open(self):
self.CONNECTION = cx_Oracle.Connection(self.CONFIG.username,
self.CONFIG.password,
self.CONFIG.db_sid)
self.CURSOR = self.CONNECTION.cursor()
....
....
....
Of course, the open method does not know what to do because cx_Oracle was defined in init and so the open method cannot see it.
I can't picture the proper way to do this, so I'm assuming I am over thinking this. What should I do instead so that open (and all other methods within the Database class) can see the imported module?
Thank you.
If you only need to use cx_Oracle within that class, you can just set it as an attribute on this instance, for example:
class Database:
def __init__(self, config):
self.CONFIG=config
sys.path.append(self.CONFIG.cx_oracle_path)
from cx_Oracle import cx_Oracle
self.cx_Oracle = cx_Oracle
self.open()
def open(self):
self.CONNECTION = self.cx_Oracle.Connection(self.CONFIG.username,
self.CONFIG.password,
self.CONFIG.db_sid)
self.CURSOR = self.CONNECTION.cursor()
As a side note, if you are creating multiple Database instances this is an odd approach, since you would end up adding multiple identical entries to sys.path.
Related
I'm using Django and Python 3.7. I have two applications in my project -- common and mainsite . In common, I have a file "model_utils.py" at the root of my application (right next to models.py). It contains this code
class RawCol(Expression):
def __init__(self, model, field_name):
field = model._meta.get_field(field_name)
self.table = model._meta.db_table
self.column = field.column
super().__init__(output_field=CharField())
def as_sql(self, compiler, connection):
sql = f'"{self.table}"."{self.column}"'
return sql, []
How do I reference this class from my other application, "mainsite"? I put this at the top of one of my files in mainsite ..
from common import RawCol
but when I run some tests, I get this error ...
ImportError: cannot import name 'RawCol' from 'common' (/Users/davea/Documents/workspace/mainsite_project/common/__init__.py)
Edit: Project structure at the top level directories looks like ...
+ common
+ mainsite
+ mainsite_project
+ manage.py
+ templates
+ venv
Try from common.model_utils import RawCol
instead of from common import RawCol
You always need to specify the exact .py file (without the .py ending) to import from.
If it still doesn't work, it can be a circular import problem.
If you try to import something from mainsite.model into common.model_utils and the other way around, you are creating an impossible import loop.
You can fix this by creating a seperate file like common/dependent_model.py and putting only the RawCol() class in there without any import from mainsite. Like this, the both files are not importing from each other (which doesn't work).
I am diving down into the world of Python, practicing by writing a simple inventory based application for my dvd collection as a means to get aquainted to working with sqlite3.
As part of my project, I am using a ini file for settings, and then reading the values from that in a shared library that is called from another file. I am curious as to feedback on my methods, especially my use of the config file, and best coding practices around it.
The config is formatted as follows, named config.ini
[main]
appversion = 0.1.0
datasource = data\database.db
my utils library is then formatted as follows:
import os
import sqlite3
from configparser import ConfigParser
CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'config/config.ini')
def get_settings(config_path=CONFIG_PATH):
config = ConfigParser()
config.read(config_path)
return config
def db_connect():
config = get_settings()
con = sqlite3.connect(config.get('main', 'datasource'))
return con
Finally, my test lookup, which does function is:
from utils import db_connect
def asset_lookup():
con = db_connect()
cur = con.cursor()
cur.execute("SELECT * FROM dvd")
results = cur.fetchall()
for row in results:
print(row)
My biggest question is in regards to my building of the data connection from within utils.py. First I'm reading the file, then form within the same script, building the data connection from a setting within the ini file. This is then read by other files. This was my method of trying to be modular, but I was not sure if its proper.
Thanks in advance.
To directly answer your question, you could do something like this to cache your objects so you don't create/open them over and over again whenever you call one of the functions in utils.py:
import os
import sqlite3
from configparser import ConfigParser
CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'config/config.ini')
config = None
con = None
def get_settings(CONFIG_PATH):
global config
if config is None:
config = ConfigParser()
config.read(CONFIG_PATH)
return config
def db_connect():
global con
if con is None:
config = get_settings()
con = sqlite3.connect(config.get('main', 'datasource'))
return con
While this might solve your problem, it relies heavily on global variables, which might cause problems elsewhere. Typically, that's where you switch to classes as containers for your code parts that belong together. For example:
import os
import sqlite3
from configparser import ConfigParser
class DVDApp:
CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'config/config.ini')
def __init__(self):
self.config = ConfigParser()
self.config.read(self.CONFIG_PATH)
self.con = sqlite3.connect(self.config.get('main', 'datasource'))
def asset_lookup(self):
cur = self.con.cursor()
cur.execute("SELECT * FROM dvd")
results = cur.fetchall()
for row in results:
print(row)
Initializing config and connection objects held in self reduces to just 3 lines of code. Thereby making it almost unnecessary to split your code over several files. And even if so, it would be enough to share the one instance of DVDApp between modules, which then holds all the other shared objects.
Let's assume, that there are following minimalistic python classes inside one module, e.g. Module:
module/
__init__.py
db.py
document.py
db.py
import yaml
class DB(object):
config = {}
#classmethod
def load_config(cls, config_path):
cls.config = yaml.load(open(config_path, 'r').read())
and document.py
from .db import DB
class Document(object):
db = None
def __init__(self):
self.db = DB()
End-user is going to use such Module as follows:
from Module import DB, Document
DB.load_config('/path/to/config.yml')
Document.do_some_stuff()
doc1 = Document()
doc2 = Document.find(...)
doc2.update_something(...)
doc2.save()
It is expected that Document class and every instance of it will have internally an access to class DB with a config specified by user. However, since Document performs an internal import of DB class (from .db import DB) it receives a 'fresh' DB class with default config.
I did a lot of searches, most of questions and answers are about module-wide configs, but not specified by the end user.
How can I achieve such functionality? I guess that there is some architectural problem here, but what is the most simple way to solve it?
Perhaps this isn't the most appropriate answer, but a few months back I wrote a module called aconf for this exact purpose. It's a memory-based global configuration module for Python written in 8 lines. The idea is you can do the following:
You create a Config object to force the user to input the configuration your program requires (in this case it's inside config.py):
""" 'Config' class to hold our desired configuration parameters.
Note:
This is technically not needed. We do this so that the user knows what he/she should pass
as a config for the specific project. Note how we also take in a function object - this is
to demonstrate that one can have absolutely any type in the global config and is not subjected
to any limitations.
"""
from aconf import make_config
class Config:
def __init__(self, arg, func):
make_config(arg=arg, func=func)
You consume your configuration throughout your module (in this case, inside functionality.py):
""" Use of the global configuration through the `conf` function. """
from aconf import conf
class Example:
def __init__(self):
func = conf().func
arg = conf().arg
self.arg = func(arg)
And then use it (in this case inside main.py):
from project.config import Config
from project.functionality import Example
# Random function to demonstrate we can pass _anything_ to 'make_config' inside 'Config'.
def uppercase(words):
return words.upper()
# We create our custom configuration without saving it.
Config(arg="hello world", func=uppercase)
# We initialize our Example object without passing the 'Config' object to it.
example = Example()
print(example.arg)
# >>> "HELLO WORLD"
The entire aconf module is the following:
__version__ = "1.0.1"
import namedtupled
def make_config(**kwargs):
globals()["aconf"] = kwargs
conf = lambda: namedtupled.map(globals()["aconf"])
config = lambda: globals()["aconf"]
... in essence, you just save your configuration to globals() during runtime.
It's so stupid it makes me wonder if you should even be allowed to do this. I wrote aconf for fun, but have never personally used it in a big project. The reality is, you might run into the problem of making your code weird for other developers.
I am using jython with a third party application. The third party application has some builtin libraries foo. To do some (unit) testing we want to run some code outside of the application. Since foo is bound to the application we decided to write our own mock implementation.
However there is one issue, we implemented our mock class in python while their class is in java. Thus to use their code one would do import foo and foo is the mock class afterwards. However if we import the python module like this we get the module attached to the name, thus one has to write foo.foo to get to the class.
For convenience reason we would love to be able to write from ourlib.thirdparty import foo to bind foo to the foo-class. However we would like to avoid to import all the classes in ourlib.thirdparty directly, since the loading time for each file takes quite a while.
Is there any way to this in python? ( I did not get far with Import hooks I tried simply returning the class from load_module or overwriting what I write to sys.modules (I think both approaches are ugly, particularly the later))
edit:
ok: here is what the files in ourlib.thirdparty look like simplified(without magic):
foo.py:
try:
import foo
except ImportError:
class foo
....
Actually they look like this:
foo.py:
class foo
....
__init__.py in ourlib.thirdparty
import sys
import os.path
import imp
#TODO: 3.0 importlib.util abstract base classes could greatly simplify this code or make it prettier.
class Importer(object):
def __init__(self, path_entry):
if not path_entry.startswith(os.path.join(os.path.dirname(__file__), 'thirdparty')):
raise ImportError('Custom importer only for thirdparty objects')
self._importTuples = {}
def find_module(self, fullname):
module = fullname.rpartition('.')[2]
try:
if fullname not in self._importTuples:
fileObj, self._importTuples[fullname] = imp.find_module(module)
if isinstance(fileObj, file):
fileObj.close()
except:
print 'backup'
path = os.path.join(os.path.join(os.path.dirname(__file__), 'thirdparty'), module+'.py')
if not os.path.isfile(path):
return None
raise ImportError("Could not find dummy class for %s (%s)\n(searched:%s)" % (module, fullname, path))
self._importTuples[fullname] = path, ('.py', 'r', imp.PY_SOURCE)
return self
def load_module(self, fullname):
fp = None
python = False
print fullname
if self._importTuples[fullname][1][2] in (imp.PY_SOURCE, imp.PY_COMPILED, imp.PY_FROZEN):
fp = open( self._importTuples[fullname][0], self._importTuples[fullname][1][1])
python = True
try:
imp.load_module(fullname, fp, *self._importTuples[fullname])
finally:
if python:
module = fullname.rpartition('.')[2]
#setattr(sys.modules[fullname], module, getattr(sys.modules[fullname], module))
#sys.modules[fullname] = getattr(sys.modules[fullname], module)
if isinstance(fp, file):
fp.close()
return getattr(sys.modules[fullname], module)
sys.path_hooks.append(Importer)
As others have remarked, it is such a plain thing in Python that the import statement iself has a syntax for that:
from foo import foo as original_foo, for example -
or even import foo as module_foo
Interesting to note is that the import statemente binds a name to the imported module or object ont he local context - however, the dictionary sys.modules (on the moduels sys of course), is a live reference to all imported modules, using their names as a key. This mechanism plays a key role in avoding that Python re-reads and re-executes and already imported module , when running (that is, if various of yoru modules or sub-modules import the samefoo` module, it is just read once -- the subsequent imports use the reference stored in sys.modules).
And -- besides the "import...as" syntax, modules in Python are just another object: you can assign any other name to them in run time.
So, the following code would also work perfectly for you:
import foo
original_foo = foo
class foo(Mock):
...
There are given two versions of a storage. Based on the version, I need to select the proper interface module to get the result.
The file structure looks like this:
lib/
__init__.py
provider.py
connection.py
device.py
storage/
__init__.py
interface_v1.py # interface for storage of version 1
interface_v2.py # interface for storage of version 2
main.py
The main.py imports provider.py, that should import one of the interfaces listed in the storage subpackage depending on the version of the storage.
main.py:
from lib.provider import Provider
from lib.connection import Connection
from lib.device import Device
connection = Connection.establish(Device)
storage_version = Device.get_storage_version()
massage = Provider.get_data(connection)
provider.py should import an interface to the storage based on storage_version and implement provide some functions:
from storage import interface
class Provider(object):
def __init_(self):
self.storage = interface.Storage
def get_data(self, connection):
return self.storage.get_data()
def clear_storage(self, connection):
self.storage.clear_storage()
This example is not complete, but should be sufficient for the problem explanation.
Additional question:
Is it possible to use storage.__init__ to use import just the
subpackage?
How to proper implement Factory in Python?
Assuming the interface_v1 and interface_v2 bith impleement a class StorageClassI guess something like this:
import storage.interface_v1
import storage.interface_v2
class Provider(object):
def __init__(self , version):
if version == 1:
self.storage = storage.interface_v1.StorageClass
else:
self.storage = storage.interface_v2.StorageClass
Would be the best solution - but https://docs.python.org/2/library/functions.html#import should provide a way to import a module based on name.