from ast import literal_eval
import imp
import logging
import os
import re
import sys
import subprocess
import tempfile
import shutil
__all__ = [
'update_config',
'detect_module',
'module_exists',
'get_dependencies',
'find_files',
'install_requirements',
'coverage_modules_path'
]
[docs]def update_config(config, **kwargs):
"""Updates config dictionary from keyword arguments.
"""
for key, value in kwargs.iteritems():
config[key] = value
return config
[docs]def detect_module(path):
"""Detect if a path is part of a openerp module or not
:param path: to examine
:return: None if is not a module or the module name
"""
stack = path.split(os.path.sep)
if not stack[0]:
stack[0] = os.path.sep
stack = [x for x in stack if x]
while stack:
path = os.path.join(*stack)
module = stack.pop()
if not os.path.isdir(path):
continue
files = os.listdir(path)
if '__terp__.py' in files:
return module
return None
[docs]def module_exists(module):
"""Check if a python module exists.
This is used to check if a module have its own tests defined, Eg:
`addons.module_name.tests`
:param module: Module name to check
:return: True if exists or False if not
"""
modlist = module.split('.')
pathlist = None
for mod in modlist:
try:
openfile, pathname, desc = imp.find_module(mod, pathlist)
pathlist = [pathname]
# Clean netsvc Services
import netsvc
netsvc.SERVICES.clear()
except ImportError:
return False
else:
if openfile:
openfile.close()
return True
[docs]def get_dependencies(module, addons_path=None, deps=None):
"""Get all the dependencies of a module without database
Using `__terp__.py` files and is used to check requirements.txt in the
dependencies.
:param module: Module to find the dependencies
:param addons_path: Path to find the modules
:return: a listt of dependencies.
"""
if deps is None:
deps = []
if addons_path is None:
from destral.openerp import OpenERPService
service = OpenERPService()
addons_path = service.config['addons_path']
pj = os.path.join
module_path = pj(addons_path, module)
if not os.path.exists(module_path):
raise Exception('Module {} not found in {}'.format(
module, addons_path
))
terp_path = pj(module_path, '__terp__.py')
if not os.path.exists(terp_path):
raise Exception(
'Module {} is not a valid module. Missing __terp__.py file'.format(
module
)
)
with open(terp_path, 'r') as terp_file:
terp = literal_eval(terp_file.read())
for dep in terp['depends']:
deps.append(dep)
deps += get_dependencies(dep, addons_path, deps)
return list(set(deps))
[docs]def find_files(diff):
"""Return all the files implicated in a diff
"""
paths = []
for line in re.findall("--- a/.*|\+\+\+ b/.*", diff):
line = '/'.join(line.split('/')[1:])
paths.append(line)
return list(set(paths))
[docs]def install_requirements(module, addons_path):
"""Install module requirements and its dependecies
"""
logger = logging.getLogger('destral.utils')
modules_requirements = get_dependencies(module, addons_path)
modules_requirements.append(module)
for module_requirements in modules_requirements:
req = os.path.join(
addons_path,
module_requirements,
'requirements.txt'
)
pip = os.path.join(sys.prefix, 'bin', 'pip')
if os.path.exists(req) and os.path.exists(pip):
logger.info('Requirements file %s found. Installing...', req)
subprocess.check_call([pip, "install", "-r", req])
def coverage_modules_path(modules_to_test, addons_path):
return [
os.path.relpath(os.path.realpath(os.path.join(addons_path, m))) for m in
modules_to_test
]
def read_po(po_path):
"""
Read a Po file
:param po_path: .po path
:return: po content
"""
from babel.messages import pofile
logger = logging.getLogger('destral.utils.read_po')
try:
with open(po_path, 'r') as pot:
catalog = pofile.read_po(pot)
return catalog
except ValueError:
# If bad formatted data, replace it
with open(po_path, 'r') as potB:
data = potB.read()
from re import sub, findall
from dateutil.parser import parse as date_parse
from babel.messages import Catalog
replace_string = r'\1 {}\2'.format(
date_parse(
findall(r"POT-Creation-Date: (.*)\\n", data)[0]
).strftime('%Y-%m-%d %H:%M')
)
data = sub(r"(POT-Creation-Date:).*(\\n)", replace_string, data)
replace_string = r'\1 {}\2'.format(
date_parse(
findall(r"PO-Revision-Date: (.*)\\n", data)[0]
).strftime('%Y-%m-%d %H:%M')
)
data = sub(r"(PO-Revision-Date:).*(\\n)", replace_string, data)
catalog = Catalog()
parser = pofile.PoFileParser(catalog)
parser.parse(data.split('\n'))
logger.warning(
'Data of POfile {} has bad formatted '
'creation or revision dates'.format(po_path)
)
return catalog
def compare_pofiles(pathA, pathB):
"""
:param pathA: path to pot/po file
:param pathB: path to pot/po file
:param translate: whether translation should be checked or not
:return: True if all strings in pathA are in pathB
"""
from os.path import isfile
import logging
logger = logging.getLogger('destral.utils.compare_pofiles')
if not isfile(pathA):
logger.info('Could not get po/pot file: {}'.format(pathA))
return None, None
elif not isfile(pathB):
logger.info('Could not get po/pot file: {}'.format(pathB))
return None, None
fileA = read_po(pathA)
fileB = read_po(pathB)
not_found = []
not_translated = []
for msgA in fileA:
if msgA.id == '':
continue
msgB = fileB.get(msgA.id)
if not msgB:
not_found.append(msgA.id)
continue
if not msgB.string:
not_translated.append(msgA.id)
return not_found, not_translated
class TempDir(object):
def __init__(self):
self.dir = tempfile.mkdtemp()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
shutil.rmtree(self.dir)