Getting Import/Library issues in my robot framework, I've Customlib file where all my custom functions reside while trying to import the Customlib getting an error
[enter image description here][1]
[enter image description here][2]
[1]: https://i.stack.imgur.com/poPzQ.png
[2]: https://i.stack.imgur.com/qkbxK.png
CustomLib Code:
robot is complaining Setup failed: No keyword with name Customlib.get config test data
import os
# Declaring empty list for test data and config file
testData = {}
configTestData = {}
class CustomLib:
ROBOT_LIBRARY_SCOPE = 'Test Case'
# Function for getting data from confi file and test data file altogether
#staticmethod
def get_global_config_data_and_test_data(testdata_filename):
configpath = os.path.dirname(os.path.abspath(__file__))
print(configpath)
configpath1 = configpath.replace("Utils", "")
configpath = configpath1.replace(configpath1, "config.properties")
# configpath=configpath.replace("Utils", "config.properties")
try:
file = open(configpath)
for line in file:
content = line.split("=")
firstArgument = content[0]
secondArgument = content[1]
a = firstArgument.rstrip('\n')
b = secondArgument.rstrip('\n')
testData[a] = b
except Exception as e:
if hasattr(e, 'message'):
print(e.message)
else:
print(e)
finally:
file.close()
return CustomLib.get_testData_From_PropertiesFile(CustomLib.OS_path_fromat_separator(testdata_filename))
# Function for reading test data from property file
#staticmethod
def get_testData_From_PropertiesFile(propfile):
try:
file = open(propfile)
for line in file:
content = line.split("=")
firstArgument = content[0]
secondArgument = content[1]
a = firstArgument.rstrip('\n')
b = secondArgument.rstrip('\n')
testData[a] = b
except Exception as e:
if hasattr(e, 'message'):
print(e.message)
else:
print(e)
finally:
file.close()
return testData
# FUnction for generating dynamic path which has text in XPATH
def generate_dynamic_xpath(self, locatorvalue, replacement):
after_replacement = locatorvalue.replace('#', replacement)
return after_replacement
# Function for creating report name
def create_report_name(self, testContent, date):
reportname = testContent.replace('date', date)
return reportname
# Function for reading config.properties file
#staticmethod
def get_config_testdata():
configpath = os.path.dirname(os.path.abspath(__file__))
print(configpath)
configpath1 = configpath.replace("Utils", "")
configpath = configpath1.replace(configpath1, "config.properties")
# configpath=configpath.replace("Utils", "config.properties")
print(configpath)
try:
file = open(configpath)
for line in file:
content = line.split("=")
firstArgument = content[0]
secondArgument = content[1]
a = firstArgument.rstrip('\n')
b = secondArgument.rstrip('\n')
configTestData[a] = b
except Exception as e:
if hasattr(e, 'Exception occured while reading properties file'):
print(e.message)
else:
print(e)
finally:
file.close()
return configTestData
# Function to format the path for different OS
#staticmethod
def OS_path_fromat_separator(pathformat):
config_data = {}
config_data = CustomLib.get_config_testdata()
if (config_data['OS'] == 'Windows'):
OSPath = pathformat.replace('$', '//')
return OSPath
else:
OSPath = pathformat.replace('$', '/')
return OSPath
# Function for generating dynamic CSS on the basis of text
def generate_dynamic_CSS(self, locatorvalue, replacement):
after_replacement = locatorvalue.replace('#', replacement)
return after_replacement
def main():
CustomLib.get_global_config_data()
# CustomLib.get_testData_From_PropertiesFile()
if __name__ == '__main__':
main()
I'm using relative path to import custom library. Below is the reference, how to use import using relative path. So this should work.
Library ..${/}foldername${/}customlibrary.py
And you can also use full path and import the library
I'm trying to make application using PyQt5, and I'm using QWebEngine for the user interface.
I have successfully made the application but now I want to make a plugin mechanism for this application so that later it will be easier to add features. I have tried doing it using Yapsy, but then I realized that I could not use the relative import from the plugin i made
So i decide to create it on my own by using importlib module, then i found this question
Using importlib to dynamically import module(s) containing relative imports
while the answer itself is not wrong, but it doesn't work in my case where i want to load a module outside of my package directory
my source code can be found here
As you can see in my source code there is 2 plugins directory, plugins inside myapp package and outside myapp package. The plugins directory inside myapp package is there so that it won't gave me any ImportError when it try to import myapp.plugins
Now the problem is when i run it using python3 -m myapp it will give me ImportError $No module named 'myapp.plugins.extras'. This is where i'm stuck.
After googling for a while i found this article
Dependency Injection with Import Hooks in Python 3
while the concept itself is exactly what i need, but the way he provide the module is not what i'm expecting cuz he use a class to provide the module instead of file.
So i change it to fulfill what i need
DependencyInjector.py
import os
import io
import sys
import _imp
import marshal
import types
import importlib
import importlib._bootstrap as _bootstrap
from importlib.abc import Loader
_PYCACHE = '__pycache__'
_OPT = 'opt-'
SOURCE_SUFFIXES = ['.py'] # _setup() adds .pyw as needed.
BYTECODE_SUFFIXES = ['.pyc']
MAGIC_NUMBER = (3394).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
def _r_long(int_bytes):
"""Convert 4 bytes in little-endian to an integer."""
return int.from_bytes(int_bytes, 'little')
def _w_long(x):
"""Convert a 32-bit integer to little-endian."""
return (int(x) & 0xFFFFFFFF).to_bytes(4, 'little')
def _calc_mode(path):
"""Calculate the mode permissions for a bytecode file."""
try:
mode = os.stat(path).st_mode
except OSError:
mode = 0o666
# We always ensure write access so we can update cached files
# later even when the source files are read-only on Windows (#6074)
mode |= 0o200
return mode
def _write_atomic(path, data, mode=0o666):
# id() is used to generate a pseudo-random filename.
path_tmp = '{}.{}'.format(path, id(path))
fd = os.open(path_tmp,
os.O_EXCL | os.O_CREAT | os.O_WRONLY, mode & 0o666)
try:
with io.FileIO(fd, 'wb') as file:
file.write(data)
os.replace(path_tmp, path)
except OSError:
try:
os.unlink(path_tmp)
except OSError:
pass
raise
_code_type = type(_write_atomic.__code__)
def cache_from_source(path, debug_override=None, *, optimization=None):
if debug_override is not None:
print('the debug_override parameter is deprecated; use '
"'optimization' instead", DeprecationWarning)
if optimization is not None:
message = 'debug_override or optimization must be set to None'
raise TypeError(message)
optimization = '' if debug_override else 1
path = os.fspath(path)
head, tail = os.path.split(path)
base, sep, rest = tail.rpartition('.')
tag = sys.implementation.cache_tag
if tag is None:
raise NotImplementedError('sys.implementation.cache_tag is None')
almost_filename = ''.join([(base if base else rest), sep, tag])
if optimization is None:
if sys.flags.optimize == 0:
optimization = ''
else:
optimization = sys.flags.optimize
optimization = str(optimization)
if optimization != '':
if not optimization.isalnum():
raise ValueError('{!r} is not alphanumeric'.format(optimization))
almost_filename = '{}.{}{}'.format(almost_filename, _OPT, optimization)
return os.path.join(head, _PYCACHE, almost_filename + BYTECODE_SUFFIXES[0])
def _classify_pyc(data, name, exc_details):
magic = data[:4]
if magic != MAGIC_NUMBER:
message = f'bad magic number in {name!r}: {magic!r}'
_bootstrap._verbose_message('{}', message)
raise ImportError(message, **exc_details)
if len(data) < 16:
message = f'reached EOF while reading pyc header of {name!r}'
_bootstrap._verbose_message('{}', message)
raise EOFError(message)
flags = _r_long(data[4:8])
# Only the first two flags are defined.
if flags & ~0b11:
message = f'invalid flags {flags!r} in {name!r}'
raise ImportError(message, **exc_details)
return flags
def _validate_timestamp_pyc(data, source_mtime, source_size, name,
exc_details):
if _r_long(data[8:12]) != (source_mtime & 0xFFFFFFFF):
message = f'bytecode is stale for {name!r}'
_bootstrap._verbose_message('{}', message)
raise ImportError(message, **exc_details)
if (source_size is not None and
_r_long(data[12:16]) != (source_size & 0xFFFFFFFF)):
raise ImportError(f'bytecode is stale for {name!r}', **exc_details)
def _validate_hash_pyc(data, source_hash, name, exc_details):
if data[8:16] != source_hash:
raise ImportError(
f'hash in bytecode doesn\'t match hash of source {name!r}',
**exc_details,
)
def _compile_bytecode(data, name=None, bytecode_path=None, source_path=None):
code = marshal.loads(data)
if isinstance(code, _code_type):
_bootstrap._verbose_message('code object from {!r}', bytecode_path)
if source_path is not None:
_imp._fix_co_filename(code, source_path)
return code
else:
raise ImportError('Non-code object in {!r}'.format(bytecode_path),
name=name, path=bytecode_path)
def _code_to_timestamp_pyc(code, mtime=0, source_size=0):
data = bytearray(MAGIC_NUMBER)
data.extend(_w_long(0))
data.extend(_w_long(mtime))
data.extend(_w_long(source_size))
data.extend(marshal.dumps(code))
return data
def _code_to_hash_pyc(code, source_hash, checked=True):
data = bytearray(MAGIC_NUMBER)
flags = 0b1 | checked << 1
data.extend(_w_long(flags))
assert len(source_hash) == 8
data.extend(source_hash)
data.extend(marshal.dumps(code))
return data
class DependencyInjectorFinder(importlib.abc.MetaPathFinder):
def __init__(self, loader):
# we'll write the loader in a minute, hang tight
self._loader = loader
def find_spec(self, fullname, path, target=None):
if self._loader.provides(fullname):
return self._gen_spec(fullname)
def _gen_spec(self, fullname):
spec = importlib.machinery.ModuleSpec(fullname, self._loader)
return spec
class DependencyInjectorLoader(Loader):
_COMMON_PREFIX = "myapp.plugins."
path = None
def __init__(self):
self._services = {}
self._dummy_module = types.ModuleType(self._COMMON_PREFIX[:-1])
self._dummy_module.__path__ = []
def path_stats(self, path):
st = os.stat(path)
return {'mtime': st.st_mtime, 'size': st.st_size}
def _cache_bytecode(self, source_path, bytecode_path, data):
mode = _calc_mode(source_path)
return self.set_data(bytecode_path, data, _mode=mode)
def set_data(self, path, data, *, _mode=0o666):
parent, filename = os.path.split(path)
path_parts = []
# Figure out what directories are missing.
while parent and not os.path.isdir(parent):
parent, part = os.path.split(parent)
path_parts.append(part)
# Create needed directories.
for part in reversed(path_parts):
parent = os.path.join(parent, part)
try:
os.mkdir(parent)
except FileExistsError:
# Probably another Python process already created the dir.
continue
except OSError as exc:
# Could be a permission error, read-only filesystem: just forget
# about writing the data.
_bootstrap._verbose_message('could not create {!r}: {!r}',
parent, exc)
return
try:
_write_atomic(path, data, _mode)
_bootstrap._verbose_message('created {!r}', path)
except OSError as exc:
# Same as above: just don't write the bytecode.
_bootstrap._verbose_message('could not create {!r}: {!r}', path,
exc)
def get_filename(self, fullname):
"""Return the path to the source file as found by the finder."""
if fullname in self._services:
module = self._services[fullname]
return module.__path__
return None
def get_code(self, fullname):
"""Concrete implementation of InspectLoader.get_code.
Reading of bytecode requires path_stats to be implemented. To write
bytecode, set_data must also be implemented.
"""
source_path = self.get_filename(fullname)
source_mtime = None
source_bytes = None
source_hash = None
hash_based = False
check_source = True
try:
bytecode_path = cache_from_source(source_path)
except NotImplementedError:
bytecode_path = None
else:
try:
st = self.path_stats(source_path)
except OSError:
pass
else:
source_mtime = int(st['mtime'])
try:
data = self.get_data(bytecode_path)
except OSError:
pass
else:
exc_details = {
'name': fullname,
'path': bytecode_path,
}
try:
flags = _classify_pyc(data, fullname, exc_details)
bytes_data = memoryview(data)[16:]
hash_based = flags & 0b1 != 0
if hash_based:
check_source = flags & 0b10 != 0
if (_imp.check_hash_based_pycs != 'never' and
(check_source or
_imp.check_hash_based_pycs == 'always')):
source_bytes = self.get_data(source_path)
source_hash = _imp.source_hash(
_RAW_MAGIC_NUMBER,
source_bytes,
)
_validate_hash_pyc(data, source_hash, fullname,
exc_details)
else:
_validate_timestamp_pyc(
data,
source_mtime,
st['size'],
fullname,
exc_details,
)
except (ImportError, EOFError):
pass
else:
_bootstrap._verbose_message('{} matches {}', bytecode_path,
source_path)
return _compile_bytecode(bytes_data, name=fullname,
bytecode_path=bytecode_path,
source_path=source_path)
if source_bytes is None:
source_bytes = self.get_data(source_path)
code_object = self.source_to_code(source_bytes, source_path)
_bootstrap._verbose_message('code object from {}', source_path)
if (not sys.dont_write_bytecode and bytecode_path is not None and
source_mtime is not None):
if hash_based:
if source_hash is None:
source_hash = _imp.source_hash(source_bytes)
data = _code_to_hash_pyc(
code_object, source_hash, check_source)
else:
data = _code_to_timestamp_pyc(code_object, source_mtime,
len(source_bytes))
try:
self._cache_bytecode(source_path, bytecode_path, data)
_bootstrap._verbose_message('wrote {!r}', bytecode_path)
except NotImplementedError:
pass
return code_object
def source_to_code(self, data, path, *, _optimize=-1):
"""Return the code object compiled from source.
The 'data' argument can be any object type that compile() supports.
"""
return _bootstrap._call_with_frames_removed(compile, data, path, 'exec',
dont_inherit=True, optimize=_optimize)
def get_data(self, path):
"""Return the data from path as raw bytes."""
# TODO: raise error if the file is not found
# if it's a directory try to get the __init__.py file inside this folder
if os.path.isdir(path):
init_path = os.path.join(path, '__init__.py')
if os.path.exists(init_path):
with io.FileIO(init_path, 'r') as file:
return file.read()
with io.FileIO(path, 'r') as file:
return file.read()
def provide(self, service_name, module):
"""Register a service as provided via the given module
A service is any Python object in this context - an imported module,
a class, etc."""
self._services[service_name] = module
def provides(self, fullname):
if self._truncate_name(fullname) in self._services:
return True
else:
# this checks if we should return the dummy module,
# since this evaluates to True when importing myapp and
# myapp.virtual
return self._COMMON_PREFIX.startswith(fullname)
def create_module(self, spec):
"""Create the given module from the supplied module spec
Under the hood, this module returns a service or a dummy module,
depending on whether Python is still importing one of the names listed
in _COMMON_PREFIX.
"""
service_name = self._truncate_name(spec.name)
if service_name not in self._services:
# return our dummy module since at this point we're loading
# *something* along the lines of "myapp.virtual" that's not
# a service
return self._dummy_module
module = self._services[service_name]
return module
def exec_module(self, module):
"""Execute the given module in its own namespace
This method is required to be present by importlib.abc.Loader,
but since we know our module object is already fully-formed,
this method merely no-ops.
"""
if hasattr(module, "__path__"):
self.path = module.__path__
code = self.get_code(module.__name__)
importlib._bootstrap._call_with_frames_removed(
exec, code, module.__dict__)
def _truncate_name(self, fullname):
"""Strip off _COMMON_PREFIX from the given module name
Convenience method when checking if a service is provided.
"""
truncated_name = fullname
if truncated_name.startswith(self._COMMON_PREFIX):
truncated_name = fullname[len(self._COMMON_PREFIX):]
return truncated_name
def is_package(self, fullname):
return self.provides(fullname)
most of the source code above was taken from importlib.machinery.SourceFileLoader
PluginManager.py
import os
import re
import ast
import sys
import types
import typing
import importlib
import configparser
from PyQt5.QtCore import QObject
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineScript
from .utils import Signal, findFiles
from .config import change_filter, getInstance
from .DependencyInjector import DependencyInjectorFinder, DependencyInjectorLoader
class PluginInjector:
"""
Convenience wrapper for DependencyInjectorLoader and DependencyInjectorFinder.
"""
def __init__(self):
self._loader = DependencyInjectorLoader()
self._finder = DependencyInjectorFinder(self._loader)
self.installed = False
self.install()
def install(self):
if not self.installed:
self.installed = True
sys.meta_path.append(self._finder)
def provide(self, service_name, module):
self._loader.provide(service_name, module)
class PluginInfo(configparser.ConfigParser):
def __init__(self, filepath):
super().__init__()
self._filepath = filepath
self.read(self._filepath, encoding='utf-8')
def isValid(self) -> bool:
""""""
return self.has_section("plugin") and self.has_option("plugin", "Module")
class PluginManager(QObject):
pluginAdded = Signal()
pluginRemoved = Signal()
pluginActivated = Signal()
pluginDeactivated = Signal()
loadStarted = Signal()
loadFinished = Signal()
beforeLoad = Signal()
bridgeInitialize = Signal()
def __init__(self, pluginDirs: typing.List[str] = [], parent=None):
super().__init__(parent)
app = QApplication.instance()
from .MyApplication import MyApplication
assert isinstance(app, MyApplication)
self._injector = PluginInjector()
self._plugins = {}
self._loadedPlugins = {}
self._pluginsResources = {}
self._pluginDirs = pluginDirs
self.loadStarted.connect(self._loadStarted)
self.beforeLoad.connect(self._beforeLoad)
self.loadFinished.connect(self._loadFinished)
self.bridgeInitialize.connect(self._bridgeInitialize)
self._loadPlugins()
def _bridgeInitialize(self, page):
for name, resources in self._pluginsResources.items():
for resource in resources:
scriptName = name + "_" + os.path.basename(resource)
if resource.endswith(".js"):
injectionPoint = QWebEngineScript.DocumentReady
page.injectScript(resource, scriptName, injectionPoint)
elif resource.endswith(".css"):
injectionPoint = QWebEngineScript.DocumentReady
page.injectStylesheet(
resource, scriptName, injectionPoint)
def _beforeLoad(self, channel, page):
for name, plugin in self._plugins.items():
if 'beforeLoad' in dir(plugin):
plugin.beforeLoad(channel, page)
elif 'before_load' in dir(plugin):
plugin.before_load(channel, page)
def _loadStarted(self, page):
for name, plugin in self._plugins.items():
if 'loadStarted' in dir(plugin):
plugin.loadStarted(page)
elif 'load_started' in dir(plugin):
plugin.load_started(page)
def _loadFinished(self, page):
for name, plugin in self._plugins.items():
if 'loadFinished' in dir(plugin):
plugin.loadStarted(page)
elif 'load_finished' in dir(plugin):
plugin.load_started(page)
def addPluginPath(self, path: str):
assert os.path.isabs(path)
if not path in self._pluginDirs:
self._pluginDirs.append(path)
self._loadPlugins()
def _loadPlugin(self, pluginName):
if pluginName in self._loadedPlugins.keys():
return self._loadedPlugins[pluginName]
identities_paths = []
for directory in self._pluginDirs:
identities_paths += findFiles("*.plugin", directory)
module = None
for f in identities_paths:
info = PluginInfo(f)
name = f
if info.has_section("plugin") and info.has_option("plugin", "Name"):
name = info.get("plugin", "Name")
else:
continue
if name == pluginName:
if not info.isValid():
print(f"Plugin identity {name} is not valid, please read documentation "
"about how to write plugin.")
else:
parentdir = os.path.dirname(f)
module_path = os.path.join(
parentdir, info.get("plugin", "Module"))
if(not module_path.endswith(".py")):
module_path += ".py"
if os.path.exists(module_path):
try:
module_name = info.get(
"plugin", "Module").replace(".py", "")
parentdir = os.path.dirname(module_path)
print(f"creating namespace for plugin {name}")
# create a fake module for this plugin namespace
package = f"{name}"
module = types.ModuleType(package)
module.__path__ = parentdir
self._injector.provide(package, module)
# try to load all python file except for the main file and __init__.py
for f in findFiles("*.py", parentdir):
basename = os.path.splitext(
os.path.basename(f))[0]
if basename == module_name or basename == '__init__':
continue
tail = f[len(
parentdir + '/'):].replace(os.path.sep, '.').replace('.py', '')
package = f"{name}.{tail}"
m_path = f
print(
f"load external module for plugin {name} with name {module.__name__}")
spec = importlib.util.spec_from_file_location(
package, m_path)
module = importlib.util.module_from_spec(spec)
module.__path__ = m_path
self._injector.provide(package, module)
package = f"{name}.{module_name}"
spec = importlib.util.spec_from_file_location(
package, module_path)
module = importlib.util.module_from_spec(spec)
module.__path__ = module_path
self._injector.provide(package, module)
spec.loader.exec_module(module)
self._loadedPlugins[name] = module
except ImportError:
print(
f"Unable to load plugin module {name}")
break
else:
print(
f"module specified in {name} doesn't exists, it will be ignored.")
return module
def _loadPlugins(self):
""""""
identities_paths = []
for directory in self._pluginDirs:
identities_paths += findFiles("*.plugin", directory)
plugins: typing.List[PluginInfo] = []
for f in identities_paths:
info = PluginInfo(f)
name = f
if info.has_section("plugin") and info.has_option("plugin", "Name"):
name = info.get("plugin", "Name")
# if it's already exists it means that user just add a new plugins directory
if name in self._loadedPlugins.keys():
continue
if not info.isValid():
print(f"Plugin identity {name} is not valid, please read documentation "
"about how to write plugin.")
else:
parentdir = os.path.dirname(f)
module_path = os.path.join(
parentdir, info.get("plugin", "Module"))
if(not module_path.endswith(".py")):
module_path += ".py"
if os.path.exists(module_path):
info.set("plugin", "Path", module_path)
plugins.append(info)
else:
print(
f"module specified in {f} doesn't exists, it will be ignored.")
print(f"{len(plugins)} plugins found.")
for plugin in plugins:
try:
name = plugin.get("plugin", "Name")
module_name = plugin.get("plugin", "Module").replace(".py", "")
module_path = plugin.get("plugin", "Path")
parentdir = os.path.dirname(module_path)
print(f"creating namespace for plugin {name}")
# create a fake module for this plugin namespace
# create a fake module for this plugin namespace
package = f"{name}"
module = types.ModuleType(package)
module.__path__ = parentdir
self._injector.provide(package, module)
# try to load all python file except for the main file and __init__.py
for f in findFiles("*.py", parentdir):
basename = os.path.splitext(os.path.basename(f))[0]
if basename == module_name or basename == '__init__':
continue
tail = f[len(parentdir + '/'):].replace(os.path.sep, '.').replace('.py', '')
package = f"{name}.{tail}"
m_path = f
print(
f"load external module for plugin {name} with name {module.__name__}")
spec = importlib.util.spec_from_file_location(
package, m_path)
module = importlib.util.module_from_spec(spec)
module.__path__ = m_path
self._injector.provide(package, module)
print(f"importing main plugin module for plugin {name}")
package = f"{name}.{module_name}"
spec = importlib.util.spec_from_file_location(
package, module_path)
module = importlib.util.module_from_spec(spec)
module.__path__ = module_path
self._injector.provide(package, module)
spec.loader.exec_module(module)
self._loadedPlugins[name] = module
"""
By default plugin will be enabled if there was no plugin configuration.
"""
cfg = getInstance().get(f"plugins.{name}")
shouldLoad = True
if cfg is None:
shouldLoad = False
cfg = dict()
cfg['enabled'] = True
getInstance().set(f"plugins.{name}.enabled", True)
# if this is the first time the plugin is registered code above will trigger _pluginStateChange
# and activate it, so we don't need to activate it again here
if cfg['enabled'] and shouldLoad:
if 'activate' in dir(module):
module.activate()
self._plugins[name] = module
if plugin.has_option("plugin", "Resources"):
resources = ast.literal_eval(
plugin.get("plugin", "Resources"))
base_path = os.path.dirname(module_path)
def to_abspath(path: str):
if not os.path.isabs(path):
return os.path.join(base_path, path)
return path
resources = list(map(to_abspath, resources))
self._pluginsResources[name] = resources
except ImportError as e:
name = plugin.get("plugin", "Name")
print(
f"Unable to load plugin module {name} : ${e.msg}")
#change_filter("plugins")
def _pluginsStateChanged(self, key: str, value):
"""We only interested with the name and the value"""
res = re.findall("plugins\\.(.*)\\.enabled", key)
if key.endswith("enabled") and len(res) > 0:
name = res[0]
if not value:
self.disablePlugin(name)
elif value:
self.enablePlugin(name)
def enablePlugin(self, name: str):
print(f"enabling plugin {name}")
if not name in self._plugins.keys():
module = self._loadPlugin(name)
if module is not None:
if "activate" in dir(module):
module.activate()
self.pluginActivated.emit(name)
self._plugins[name] = module
self.pluginAdded.emit(name)
else:
print(f"Unable activate plugin {name}")
def disablePlugin(self, name: str):
""""""
print(f"disabling plugin {name}")
if name in self._plugins.keys():
module = self._plugins[name]
if "deactivate" in dir(module):
module.deactivate()
self.pluginDeactivated.emit(name)
self._plugins.pop(name, None)
self.pluginRemoved.emit(name)
while the code above is not yet perfect, but at least it's already fulfill what i need.
I am trying to copy parameters passed into a python script to a file. Here is the parameters.
["0013","1","1","\"john.dow#gmail.com\"","1","P123-ND 10Q","10Q H??C"]
I understand that there is a buffer problem and I am getting bad data into my parameters. However, I do not have control over what is being passed in. I am trying to copy, starting at the 5th parameter, the parameters into a file.
f = open(in_file_name, 'w')
for x in range(5, len(arg_list)):
f.write(arg_list[x] + '\n')
f.close()
The result of the file is below:
P123-ND 10Q
10Q H??C
Here is what it should be:
P123-ND
10Q
How can I not include the bad data? What is happening to the spaces between the valid information and the bad information?
As requested, here is the full program:
#!/bin/python
class Argument_Indices:
PRINTER_INDEX = 0
AREA_INDEX = 1
LABEL_INDEX = 2
EMAIL_INDEX = 3
RUN_TYPE_INDEX = 4
import argparse
import json
import os
from subprocess import call
import sys
from time import strftime
def _handle_args():
''' Setup and run argpars '''
parser = argparse.ArgumentParser(description='Set environment variables for and to call Program')
parser.add_argument('time_to_run', default='NOW', choices=['NOW', 'EOP'], help='when to run the report')
parser.add_argument('arguments', nargs='+', help='the remaining command line arguments')
return parser.parse_args()
def _proces_program(arg_list):
time_stamp = strftime("%d_%b_%Y_%H_%M_%S")
printer = arg_list[Argument_Indices.PRINTER_INDEX]
area = arg_list[Argument_Indices.AREA_INDEX]
label = arg_list[Argument_Indices.LABEL_INDEX]
in_file_name = "/tmp/program{0}.inp".format(time_stamp)
os.environ['INPUT_FILE'] = in_file_name
f = open(in_file_name, 'w')
for x in range(5, len(arg_list)):
f.write(arg_list[x])
f.close()
call(['./Program.bin', printer, area, label])
os.remove(in_file_name)
def main():
''' Main Function '''
arg_list = None
args = _handle_args()
if len(args.arguments) < 1:
print('Missing name of input file')
return -1
with open(args.arguments[0]) as input_file:
arg_list = json.load(input_file)
_process_program(arg_list)
return 0
if __name__ == '__main__':
if main() != 0:
print('Program run failed')
sys.exit()
For your exact case (where you're getting duplicated parameters received with some spaces in between) this would work:
received_param_list = ["0013","1","1","\"john.dow#gmail.com\"","1","P123-ND 10Q","10Q H??C"]
arg_list = [i.split(" ")[0] for i in received_param_list]
last_param = received_param_list[-1].split()[-1]
if last_param != arg_list[-1]:
arg_list.append(last_param)
for x in range(5, len(arg_list)):
print (arg_list[x])
Although there might be another simpler way
I'm very new to Ubuntu/Python/Bash/Gnome in general, so I still feel like there's a chance I'm doing something wrong, but it's been 3 days now without success...
Here's what the script is supposed to do:
* [✓] Download 1 random image from wallbase.cc
* [✓] Save it to the same directory that the script is running from
* [x] Set it as the wallpaper
There are two attempts made to set the wallpaper two using different commands and NEITHER work when in the script. There is a print statement (2nd line from the bottom) that spits out the correct terminal command because I can C&P the print result and it works fine, it just doesn't work when it's executed in the script.
#!/usr/bin/env python
import urllib2
import os
from gi.repository import Gio
response = urllib2.urlopen("http://wallbase.cc/random/12/eqeq/1366x768/0.000/100/32")
page_source = response.read()
thlink_pos = page_source.find("ico-X")
address_start = (page_source.find("href=\"", thlink_pos) + 6)
address_end = page_source.find("\"", address_start + 1)
response = urllib2.urlopen(page_source[address_start:address_end])
page_source = response.read()
bigwall_pos = page_source.find("bigwall")
address_start = (page_source.find("src=\"", bigwall_pos) + 5)
address_end = page_source.find("\"", address_start + 1)
address = page_source[address_start:address_end]
slash_pos = address.rfind("/") + 1
pic_name = address[slash_pos:]
bashCommand = "wget " + page_source[address_start:address_end]
os.system(bashCommand)
print "Does my new image exists?", os.path.exists(os.getcwd() + "/" + pic_name)
#attempt 1
settings = Gio.Settings.new("org.gnome.desktop.background")
settings.set_string("picture-uri", "file://" + os.getcwd() + "/" + pic_name)
settings.apply()
#attempt 2
bashCommand = "gsettings set org.gnome.desktop.background picture-uri file://" + os.getcwd() + "/" + pic_name
print bashCommand
os.system(bashCommand)
settings.apply()
You've successfully changed your settings, but they're still left unapplied, try next:
settings.apply()
after setting "picture-uri" string.
It works for me (Ubuntu 12.04).
I've modified your script (unrelated to your error):
#!/usr/bin/python
"""Set desktop background using random images from http://wallbase.cc
It uses `gi.repository.Gio.Settings` to set the background.
"""
import functools
import itertools
import logging
import os
import posixpath
import random
import re
import sys
import time
import urllib
import urllib2
import urlparse
from collections import namedtuple
from bs4 import BeautifulSoup # $ sudo apt-get install python-bs4
from gi.repository.Gio import Settings # pylint: disable=F0401,E0611
DEFAULT_IMAGE_DIR = os.path.expanduser('~/Pictures/backgrounds')
HTMLPAGE_SIZE_MAX = 1 << 20 # bytes
TIMEOUT_MIN = 300 # seconds
TIMEOUT_DELTA = 30 # jitter
# "Anime/Manga", "Wallpapers/General", "High Resolution Images"
CATEGORY_W, CATEGORY_WG, CATEGORY_HR = range(1, 4)
PURITY_SFW, PURITY_SKETCHY, PURITY_NSFW, PURITY_DEFAULT = 4, 2, 1, 0
DAY_IN_SECONDS = 86400
UrlRetreiveResult = namedtuple('UrlRetreiveResult', "path headers")
def set_background(image_path, check_exist=True):
"""Change desktop background to image pointed by `image_path`.
"""
if check_exist: # make sure we can read it (at this time)
with open(image_path, 'rb') as f:
f.read(1)
# prepare uri
path = os.path.abspath(image_path)
if isinstance(path, unicode): # quote() doesn't like unicode
path = path.encode('utf-8')
uri = 'file://' + urllib.quote(path)
# change background
bg_setting = Settings.new('org.gnome.desktop.background')
bg_setting.set_string('picture-uri', uri)
bg_setting.apply()
def url2filename(url):
"""Return basename corresponding to url.
>>> url2filename('http://example.com/path/to/file?opt=1')
'file'
"""
urlpath = urlparse.urlsplit(url).path # pylint: disable=E1103
basename = posixpath.basename(urllib.unquote(urlpath))
if os.path.basename(basename) != basename:
raise ValueError # refuse 'dir%5Cbasename.ext' on Windows
return basename
def download(url, dirpath, extensions=True, filename=None):
"""Download url to dirpath.
Use basename of the url path as a filename.
Create destination directory if necessary.
Use `extensions` to require the file to have an extension or any
of in a given sequence of extensions.
Return (path, headers) on success.
Don't retrieve url if path exists (headers are None in this case).
"""
if not os.path.isdir(dirpath):
os.makedirs(dirpath)
logging.info('created directory %s', dirpath)
# get filename from the url
filename = url2filename(url) if filename is None else filename
if os.path.basename(filename) != filename:
logging.critical('filename must not have path separator in it "%s"',
filename)
return
if extensions:
# require the file to have an extension
root, ext = os.path.splitext(filename)
if root and len(ext) > 1:
# require the extension to be in the list
try:
it = iter(extensions)
except TypeError:
pass
else:
if ext not in it:
logging.warn(("file extension is not in the list"
" url=%s"
" extensions=%s"),
url, extensions)
return
else:
logging.warn("file has no extension url=%s", url)
return
# download file
path = os.path.join(dirpath, filename)
logging.info("%s\n%s", url, path)
if os.path.exists(path): # don't retrieve if path exists
logging.info('path exists')
return UrlRetreiveResult(path, None)
try:
return UrlRetreiveResult(*urllib.urlretrieve(url, path,
_print_download_status))
except IOError:
logging.warn('failed to download {url} -> {path}'.format(
url=url, path=path))
def _print_download_status(block_count, block_size, total_size):
logging.debug('%10s bytes of %s', block_count * block_size, total_size)
def min_time_between_calls(min_delay):
"""Enforce minimum time delay between calls."""
def decorator(func):
lastcall = [None] # emulate nonlocal keyword
#functools.wraps(func)
def wrapper(*args, **kwargs):
if lastcall[0] is not None:
delay = time.time() - lastcall[0]
if delay < min_delay:
_sleep(min_delay - delay)
lastcall[0] = time.time()
return func(*args, **kwargs)
return wrapper
return decorator
#min_time_between_calls(5)
def _makesoup(url):
try:
logging.info(vars(url) if isinstance(url, urllib2.Request) else url)
page = urllib2.urlopen(url)
soup = BeautifulSoup(page.read(HTMLPAGE_SIZE_MAX))
return soup
except (IOError, OSError) as e:
logging.warn('failed to return soup for %s, error: %s',
getattr(url, 'get_full_url', lambda: url)(), e)
class WallbaseImages:
"""Given parameters it provides image urls to download."""
def __init__(self,
categories=None, # default; sequence of CATEGORY_*
resolution_exactly=True, # False means 'at least'
resolution=None, # all; (width, height)
aspect_ratios=None, # all; sequence eg, [(5,4),(16,9)]
purity=PURITY_DEFAULT, # combine with |
thumbs_per_page=None, # default; an integer
):
"""See usage below."""
self.categories = categories
self.resolution_exactly = resolution_exactly
self.resolution = resolution
self.aspect_ratios = aspect_ratios
self.purity = purity
self.thumbs_per_page = thumbs_per_page
def _as_request(self):
"""Create a urllib2.Request() using given parameters."""
# make url
if self.categories is not None:
categories = "".join(str(n) for n in (2, 1, 3)
if n in self.categories)
else: # default
categories = "0"
if self.resolution_exactly:
at_least_or_exactly_resolution = "eqeq"
else:
at_least_or_exactly_resolution = "gteq"
if self.resolution is not None:
resolution = "{width:d}x{height:d}".format(
width=self.resolution[0], height=self.resolution[1])
else:
resolution = "0x0"
if self.aspect_ratios is not None:
aspect_ratios = "+".join("%.2f" % (w / float(h),)
for w, h in self.aspect_ratios)
else: # default
aspect_ratios = "0"
purity = "{0:03b}".format(self.purity)
thumbs = 20 if self.thumbs_per_page is None else self.thumbs_per_page
url = ("http://wallbase.cc/random/"
"{categories}/"
"{at_least_or_exactly_resolution}/{resolution}/"
"{aspect_ratios}/"
"{purity}/{thumbs:d}").format(**locals())
logging.info(url)
# make post data
data = urllib.urlencode(dict(query='', board=categories, nsfw=purity,
res=resolution,
res_opt=at_least_or_exactly_resolution,
aspect=aspect_ratios,
thpp=thumbs))
req = urllib2.Request(url, data)
return req
def __iter__(self):
"""Yield background image urls."""
# find links to bigwall pages
# css-like: #thumbs div[class="thumb"] \
# a[class~="thlink" and href^="http://"]
soup = _makesoup(self._as_request())
if not soup:
logging.warn("can't retrieve the main page")
return
thumbs_soup = soup.find(id="thumbs")
for thumb in thumbs_soup.find_all('div', {'class': "thumb"}):
bigwall_a = thumb.find('a', {'class': "thlink",
'href': re.compile(r"^http://")})
if bigwall_a is None:
logging.warn("can't find thlink link")
continue # try the next thumb
# find image url on the bigwall page
# css-like: #bigwall > img[alt and src^="http://"]
bigwall_soup = _makesoup(bigwall_a['href'])
if bigwall_soup is not None:
bigwall = bigwall_soup.find(id='bigwall')
if bigwall is not None:
img = bigwall.find('img',
src=re.compile(r"(?i)^http://.*\.jpg$"),
alt=True)
if img is not None:
url = img['src']
filename = url2filename(url)
if filename.lower().endswith('.jpg'):
yield url, filename # successfully found image url
else:
logging.warn('suspicious url "%s"', url)
continue
logging.warn("can't parse bigwall page")
def main():
level = logging.INFO
if '-d' in sys.argv:
sys.argv.remove('-d')
level = logging.DEBUG
# configure logging
logging.basicConfig(format='%(levelname)s: %(asctime)s %(message)s',
level=level, datefmt='%Y-%m-%d %H:%M:%S %Z')
if len(sys.argv) > 1:
backgrounds_dir = sys.argv[1]
else:
backgrounds_dir = DEFAULT_IMAGE_DIR
# infinite loop: Press Ctrl+C to interrupt it
#NOTE: here's some arbitrary logic: modify for you needs e.g., break
# after the first image found
timeout = TIMEOUT_MIN # seconds
for i in itertools.cycle(xrange(timeout, DAY_IN_SECONDS)):
found = False
try:
for url, filename in WallbaseImages(
categories=[CATEGORY_WG, CATEGORY_HR, CATEGORY_W],
purity=PURITY_SFW,
thumbs_per_page=60):
res = download(url, backgrounds_dir, extensions=('.jpg',),
filename=filename)
if res and res.path:
found = True
set_background(res.path)
# don't hammer the site
timeout = max(TIMEOUT_MIN, i % DAY_IN_SECONDS)
_sleep(random.randint(timeout, timeout + TIMEOUT_DELTA))
except Exception: # pylint: disable=W0703
logging.exception('unexpected error')
_sleep(timeout)
else:
if not found:
logging.error('failed to retrieve any images')
_sleep(timeout)
timeout = (timeout * 2) % DAY_IN_SECONDS
def _sleep(timeout):
"""Add logging to time.sleep() call."""
logging.debug('sleep for %s seconds', timeout)
time.sleep(timeout)
main()
Tried to implement a python script that used the PIL library to write text on an image then update the Gnome background "picture-uri" to point to that image using the Gio class. The python script would ping pong between two images to always modify the one not in use and then attempt to "switch" by updating the Settings. Did this to avoid any flicker as modifying the current background directly drops it out temporarily. While in the shell and calling the script directly I rarely saw any issue, but in the cronjob it simply wouldn't update on the pong. I used both sync and apply and would wait several minutes before trying to switch the images. Didn't work. Tried cron as user (su -c "cmd" user) and that didn't work either.
Finally gave up on the ping pong approach when I noticed that Gnome will detect any change in the background file and update. So dropped the ping pong method and went to a temp file that I just copy over the current background using the shutil library. Works like a charm.