[FIX] P3: text model types
* remove references to basestring & unicode (use relevant pycompat helpers) * remove some str calls (either entirely or replaced by relevant helper, either text or native) * use better API to avoid unnecessary conversions * remove some XML declarations in views
This commit is contained in:
@@ -6,6 +6,7 @@ from operator import itemgetter
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import pycompat
|
||||
|
||||
FIELD_STATES = [('clear', 'Clear'), ('anonymized', 'Anonymized'), ('not_existing', 'Not Existing'), ('new', 'New')]
|
||||
ANONYMIZATION_HISTORY_STATE = [('started', 'Started'), ('done', 'Done'), ('in_exception', 'Exception occured')]
|
||||
@@ -13,7 +14,7 @@ ANONYMIZATION_DIRECTION = [('clear -> anonymized', 'clear -> anonymized'), ('ano
|
||||
|
||||
|
||||
def group(lst, cols):
|
||||
if isinstance(cols, basestring):
|
||||
if isinstance(cols, pycompat.string_types):
|
||||
cols = [cols]
|
||||
return dict((k, [v for v in itr]) for k, itr in groupby(sorted(lst, key=itemgetter(*cols)), itemgetter(*cols)))
|
||||
|
||||
|
||||
@@ -208,9 +208,9 @@ class Import(models.TransientModel):
|
||||
if cell.ctype is xlrd.XL_CELL_NUMBER:
|
||||
is_float = cell.value % 1 != 0.0
|
||||
values.append(
|
||||
unicode(cell.value)
|
||||
pycompat.text_type(cell.value)
|
||||
if is_float
|
||||
else unicode(int(cell.value))
|
||||
else pycompat.text_type(int(cell.value))
|
||||
)
|
||||
elif cell.ctype is xlrd.XL_CELL_DATE:
|
||||
is_datetime = cell.value % 1 != 0.0
|
||||
@@ -653,7 +653,7 @@ class Import(models.TransientModel):
|
||||
except ValueError as error:
|
||||
return [{
|
||||
'type': 'error',
|
||||
'message': unicode(error),
|
||||
'message': pycompat.text_type(error),
|
||||
'record': False,
|
||||
}]
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class Board(models.AbstractModel):
|
||||
@@ -45,7 +46,7 @@ class Board(models.AbstractModel):
|
||||
return node
|
||||
|
||||
def encode(s):
|
||||
if isinstance(s, unicode):
|
||||
if isinstance(s, pycompat.text_type):
|
||||
return s.encode('utf8')
|
||||
return s
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ from odoo import exceptions
|
||||
from odoo.http import Controller, request, route
|
||||
from odoo.addons.bus.models.bus import dispatch
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class BusController(Controller):
|
||||
""" Examples:
|
||||
@@ -14,7 +16,7 @@ class BusController(Controller):
|
||||
|
||||
@route('/longpolling/send', type="json", auth="public")
|
||||
def send(self, channel, message):
|
||||
if not isinstance(channel, basestring):
|
||||
if not isinstance(channel, pycompat.string_types):
|
||||
raise Exception("bus.Bus only string channels are allowed.")
|
||||
return request.env['bus.bus'].sendone(channel, message)
|
||||
|
||||
@@ -33,7 +35,7 @@ class BusController(Controller):
|
||||
options = {}
|
||||
if not dispatch:
|
||||
raise Exception("bus.Bus unavailable")
|
||||
if [c for c in channels if not isinstance(c, basestring)]:
|
||||
if [c for c in channels if not isinstance(c, pycompat.string_types)]:
|
||||
raise Exception("bus.Bus only string channels are allowed.")
|
||||
if request.registry.in_test_mode():
|
||||
raise exceptions.UserError("bus.Bus not available in test mode")
|
||||
|
||||
@@ -34,7 +34,7 @@ def calendar_id2real_id(calendar_id=None, with_date=False):
|
||||
:param with_date: if a value is passed to this param it will return dates based on value of withdate + calendar_id
|
||||
:return: real event id
|
||||
"""
|
||||
if calendar_id and isinstance(calendar_id, (basestring)):
|
||||
if calendar_id and isinstance(calendar_id, pycompat.string_types):
|
||||
res = [bit for bit in calendar_id.split('-') if bit]
|
||||
if len(res) == 2:
|
||||
real_id = res[0]
|
||||
@@ -48,7 +48,7 @@ def calendar_id2real_id(calendar_id=None, with_date=False):
|
||||
|
||||
|
||||
def get_real_ids(ids):
|
||||
if isinstance(ids, (basestring, pycompat.integer_types)):
|
||||
if isinstance(ids, (pycompat.string_types, pycompat.integer_types)):
|
||||
return calendar_id2real_id(ids)
|
||||
|
||||
if isinstance(ids, (list, tuple)):
|
||||
@@ -1060,7 +1060,7 @@ class Meeting(models.Model):
|
||||
pile.reverse()
|
||||
new_pile = []
|
||||
for item in pile:
|
||||
if not isinstance(item, basestring):
|
||||
if not isinstance(item, pycompat.string_types):
|
||||
res = item
|
||||
elif str(item) == str('&'):
|
||||
first = new_pile.pop()
|
||||
@@ -1209,7 +1209,7 @@ class Meeting(models.Model):
|
||||
|
||||
if interval == 'day':
|
||||
# Day number (1-31)
|
||||
result = unicode(date.day)
|
||||
result = pycompat.text_type(date.day)
|
||||
|
||||
elif interval == 'month':
|
||||
# Localized month name and year
|
||||
@@ -1221,6 +1221,7 @@ class Meeting(models.Model):
|
||||
|
||||
elif interval == 'time':
|
||||
# Localized time
|
||||
# FIXME: formats are specifically encoded to bytes, maybe use babel?
|
||||
dummy, format_time = self._get_date_formats()
|
||||
result = tools.ustr(date.strftime(format_time + " %Z"))
|
||||
|
||||
@@ -1316,7 +1317,7 @@ class Meeting(models.Model):
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def message_post(self, **kwargs):
|
||||
thread_id = self.id
|
||||
if isinstance(self.id, basestring):
|
||||
if isinstance(self.id, pycompat.string_types):
|
||||
thread_id = get_real_ids(self.id)
|
||||
if self.env.context.get('default_date'):
|
||||
context = dict(self.env.context)
|
||||
@@ -1352,7 +1353,7 @@ class Meeting(models.Model):
|
||||
for arg in args:
|
||||
if arg[0] == 'id':
|
||||
for n, calendar_id in enumerate(arg[2]):
|
||||
if isinstance(calendar_id, basestring):
|
||||
if isinstance(calendar_id, pycompat.string_types):
|
||||
arg[2][n] = calendar_id.split('-')[0]
|
||||
return super(Meeting, self)._name_search(name=name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
|
||||
|
||||
@@ -1390,7 +1391,7 @@ class Meeting(models.Model):
|
||||
# set end_date for calendar searching
|
||||
if any(field in values for field in ['recurrency', 'end_type', 'count', 'rrule_type', 'start', 'stop']):
|
||||
for real_meeting in real_meetings:
|
||||
if real_meeting.recurrency and real_meeting.end_type in ('count', unicode('count')):
|
||||
if real_meeting.recurrency and real_meeting.end_type == u'count':
|
||||
final_date = real_meeting._get_recurrency_end_date()
|
||||
super(Meeting, real_meeting).write({'final_date': final_date})
|
||||
|
||||
@@ -1474,7 +1475,7 @@ class Meeting(models.Model):
|
||||
for calendar_id, real_id in select:
|
||||
res = real_data[real_id].copy()
|
||||
ls = calendar_id2real_id(calendar_id, with_date=res and res.get('duration', 0) > 0 and res.get('duration') or 1)
|
||||
if not isinstance(ls, (basestring, pycompat.integer_types)) and len(ls) >= 2:
|
||||
if not isinstance(ls, (pycompat.string_types, pycompat.integer_types)) and len(ls) >= 2:
|
||||
res['start'] = ls[1]
|
||||
res['stop'] = ls[2]
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ from odoo import api, models
|
||||
|
||||
from odoo.addons.calendar.models.calendar import get_real_ids
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class Attachment(models.Model):
|
||||
|
||||
@@ -16,13 +18,13 @@ class Attachment(models.Model):
|
||||
args = list(args)
|
||||
if any([leaf for leaf in args if leaf[0] == "res_model" and leaf[2] == 'calendar.event']):
|
||||
for index in range(len(args)):
|
||||
if args[index][0] == "res_id" and isinstance(args[index][2], basestring):
|
||||
if args[index][0] == "res_id" and isinstance(args[index][2], pycompat.string_types):
|
||||
args[index] = (args[index][0], args[index][1], get_real_ids(args[index][2]))
|
||||
return super(Attachment, self).search(args, offset=offset, limit=limit, order=order, count=count)
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
""" When posting an attachment (new or not), convert the virtual ids in real ids. """
|
||||
if isinstance(vals.get('res_id'), basestring):
|
||||
if isinstance(vals.get('res_id'), pycompat.string_types):
|
||||
vals['res_id'] = get_real_ids(vals.get('res_id'))
|
||||
return super(Attachment, self).write(vals)
|
||||
|
||||
@@ -5,6 +5,8 @@ from odoo import api, models
|
||||
|
||||
from odoo.addons.calendar.models.calendar import get_real_ids
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class Message(models.Model):
|
||||
|
||||
@@ -16,7 +18,7 @@ class Message(models.Model):
|
||||
args = list(args)
|
||||
for index in range(len(args)):
|
||||
if args[index][0] == "res_id":
|
||||
if isinstance(args[index][2], basestring):
|
||||
if isinstance(args[index][2], pycompat.string_types):
|
||||
args[index] = (args[index][0], args[index][1], get_real_ids(args[index][2]))
|
||||
elif isinstance(args[index][2], list):
|
||||
args[index] = (args[index][0], args[index][1], [get_real_ids(x) for x in args[index][2]])
|
||||
|
||||
@@ -39,7 +39,7 @@ def html_entity_decode(string):
|
||||
|
||||
|
||||
def sanitize_email(email):
|
||||
assert isinstance(email, basestring) and email
|
||||
assert isinstance(email, pycompat.string_types) and email
|
||||
|
||||
result = re.subn(r';|/|:', ',', html_entity_decode(email or ''))[0].split(',')
|
||||
|
||||
|
||||
@@ -11,19 +11,6 @@ from odoo import api, models
|
||||
_logger = logging.getLogger(__name__)
|
||||
FTYPES = ['docx', 'pptx', 'xlsx', 'opendoc', 'pdf']
|
||||
|
||||
# Keep function in case it is necessary to do toUnicode(buf.encode('ascii', 'replace'))
|
||||
def toUnicode(s):
|
||||
try:
|
||||
return s.decode('utf-8')
|
||||
except UnicodeError:
|
||||
try:
|
||||
return s.decode('latin')
|
||||
except UnicodeError:
|
||||
try:
|
||||
return s.encode('ascii')
|
||||
except UnicodeError:
|
||||
return s
|
||||
|
||||
def textToString(element):
|
||||
buff = u""
|
||||
for node in element.childNodes:
|
||||
|
||||
@@ -12,6 +12,8 @@ from hashlib import md5
|
||||
from PIL import Image
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
try:
|
||||
import jcconv
|
||||
except ImportError:
|
||||
@@ -27,7 +29,7 @@ from .exceptions import *
|
||||
|
||||
def utfstr(stuff):
|
||||
""" converts stuff to string and does without failing if stuff is a utf8 string """
|
||||
if isinstance(stuff,basestring):
|
||||
if isinstance(stuff,pycompat.string_types):
|
||||
return stuff
|
||||
else:
|
||||
return str(stuff)
|
||||
|
||||
@@ -6,6 +6,7 @@ from xml.etree import ElementTree as ET
|
||||
from collections import namedtuple
|
||||
|
||||
from odoo import api, exceptions, fields, models, _
|
||||
from odoo.tools.pycompat import text_type
|
||||
|
||||
INTRASTAT_XMLNS = 'http://www.onegate.eu/2010-01-01'
|
||||
|
||||
@@ -264,18 +265,18 @@ class XmlDeclaration(models.TransientModel):
|
||||
continue
|
||||
numlgn += 1
|
||||
item = ET.SubElement(datas, 'Item')
|
||||
self._set_Dim(item, 'EXSEQCODE', unicode(numlgn))
|
||||
self._set_Dim(item, 'EXTRF', unicode(linekey.EXTRF))
|
||||
self._set_Dim(item, 'EXCNT', unicode(linekey.EXCNT))
|
||||
self._set_Dim(item, 'EXTTA', unicode(linekey.EXTTA))
|
||||
self._set_Dim(item, 'EXREG', unicode(linekey.EXREG))
|
||||
self._set_Dim(item, 'EXTGO', unicode(linekey.EXGO))
|
||||
self._set_Dim(item, 'EXSEQCODE', text_type(numlgn))
|
||||
self._set_Dim(item, 'EXTRF', text_type(linekey.EXTRF))
|
||||
self._set_Dim(item, 'EXCNT', text_type(linekey.EXCNT))
|
||||
self._set_Dim(item, 'EXTTA', text_type(linekey.EXTTA))
|
||||
self._set_Dim(item, 'EXREG', text_type(linekey.EXREG))
|
||||
self._set_Dim(item, 'EXTGO', text_type(linekey.EXGO))
|
||||
if extendedmode:
|
||||
self._set_Dim(item, 'EXTPC', unicode(linekey.EXTPC))
|
||||
self._set_Dim(item, 'EXDELTRM', unicode(linekey.EXDELTRM))
|
||||
self._set_Dim(item, 'EXTXVAL', unicode(round(amounts[0], 0)).replace(".", ","))
|
||||
self._set_Dim(item, 'EXWEIGHT', unicode(round(amounts[1], 0)).replace(".", ","))
|
||||
self._set_Dim(item, 'EXUNITS', unicode(round(amounts[2], 0)).replace(".", ","))
|
||||
self._set_Dim(item, 'EXTPC', text_type(linekey.EXTPC))
|
||||
self._set_Dim(item, 'EXDELTRM', text_type(linekey.EXDELTRM))
|
||||
self._set_Dim(item, 'EXTXVAL', text_type(round(amounts[0], 0)).replace(".", ","))
|
||||
self._set_Dim(item, 'EXWEIGHT', text_type(round(amounts[1], 0)).replace(".", ","))
|
||||
self._set_Dim(item, 'EXUNITS', text_type(round(amounts[2], 0)).replace(".", ","))
|
||||
|
||||
if numlgn == 0:
|
||||
#no datas
|
||||
|
||||
@@ -5,12 +5,11 @@ import xlrd
|
||||
from odoo.tools import pycompat
|
||||
|
||||
def _e(s):
|
||||
if type(s) is unicode:
|
||||
return s.encode('utf8')
|
||||
elif s is None:
|
||||
if s is None:
|
||||
return ''
|
||||
else:
|
||||
return str(s)
|
||||
if pycompat.PY2 and type(s) == pycompat.text_type:
|
||||
return s.encode('utf8')
|
||||
return str(s)
|
||||
|
||||
|
||||
def _is_true(s):
|
||||
|
||||
@@ -13,7 +13,7 @@ from odoo import api, http, registry, SUPERUSER_ID, _
|
||||
from odoo.addons.web.controllers.main import binary_content
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.http import request
|
||||
from odoo.tools import consteq
|
||||
from odoo.tools import consteq, pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -182,7 +182,7 @@ class MailController(http.Controller):
|
||||
else:
|
||||
# either a wrong message_id, either someone trying ids -> just go to messaging
|
||||
return self._redirect_to_messaging()
|
||||
elif res_id and isinstance(res_id, basestring):
|
||||
elif res_id and isinstance(res_id, pycompat.string_types):
|
||||
res_id = int(res_id)
|
||||
|
||||
return self._redirect_to_record(model, res_id, access_token)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class IrModel(models.Model):
|
||||
@@ -41,7 +42,7 @@ class IrModel(models.Model):
|
||||
model_class = super(IrModel, self)._instanciate(model_data)
|
||||
if model_data.get('is_mail_thread') and model_class._name != 'mail.thread':
|
||||
parents = model_class._inherit or []
|
||||
parents = [parents] if isinstance(parents, basestring) else parents
|
||||
parents = [parents] if isinstance(parents, pycompat.string_types) else parents
|
||||
model_class._inherit = parents + ['mail.thread']
|
||||
return model_class
|
||||
|
||||
|
||||
@@ -423,7 +423,7 @@ class MailThread(models.AbstractModel):
|
||||
for field_name, (template, post_kwargs) in pycompat.items(templates):
|
||||
if not template:
|
||||
continue
|
||||
if isinstance(template, basestring):
|
||||
if isinstance(template, pycompat.string_types):
|
||||
self.message_post_with_view(template, **post_kwargs)
|
||||
else:
|
||||
self.message_post_with_template(template.id, **post_kwargs)
|
||||
@@ -1268,12 +1268,15 @@ class MailThread(models.AbstractModel):
|
||||
# convert it to utf-8 for transport between the mailgate script and here.
|
||||
if isinstance(message, xmlrpclib.Binary):
|
||||
message = bytes(message.data)
|
||||
# FIXME: message_from_string parses from a *native string*
|
||||
# Warning: message_from_string doesn't always work correctly on unicode,
|
||||
# we must use utf-8 strings here :-(
|
||||
if isinstance(message, unicode):
|
||||
# message_from_string parses from a *native string*, except apparently
|
||||
# sometimes message is ISO-8859-1 binary data or some shit and the
|
||||
# straightforward version (pycompat.to_native) won't work right ->
|
||||
# always encode message to bytes then use the relevant method
|
||||
# depending on ~python version
|
||||
if isinstance(message, pycompat.text_type):
|
||||
message = message.encode('utf-8')
|
||||
msg_txt = email.message_from_string(message)
|
||||
extract = getattr(email, 'message_from_bytes', email.message_from_string)
|
||||
msg_txt = extract(message)
|
||||
|
||||
# parse the message, verify we are not in a loop by checking message_id is not duplicated
|
||||
msg = self.message_parse(msg_txt, save_original=save_original)
|
||||
@@ -1494,9 +1497,9 @@ class MailThread(models.AbstractModel):
|
||||
'message_type': 'email',
|
||||
}
|
||||
if not isinstance(message, Message):
|
||||
if isinstance(message, unicode):
|
||||
# Warning: message_from_string doesn't always work correctly on unicode,
|
||||
# we must use utf-8 strings here :-(
|
||||
# message_from_string works on a native str, so on py2 we need to
|
||||
# encode incoming unicode strings
|
||||
if pycompat.PY2 and not isinstance(message, str):
|
||||
message = message.encode('utf-8')
|
||||
message = email.message_from_string(message)
|
||||
|
||||
@@ -1725,11 +1728,11 @@ class MailThread(models.AbstractModel):
|
||||
cid = info and info.get('cid')
|
||||
else:
|
||||
continue
|
||||
if isinstance(content, unicode):
|
||||
if isinstance(content, pycompat.text_type):
|
||||
content = content.encode('utf-8')
|
||||
data_attach = {
|
||||
'name': name,
|
||||
'datas': base64.b64encode(str(content)),
|
||||
'datas': base64.b64encode(content),
|
||||
'type': 'binary',
|
||||
'datas_fname': name,
|
||||
'description': name,
|
||||
@@ -1922,7 +1925,7 @@ class MailThread(models.AbstractModel):
|
||||
values['slug'] = slug
|
||||
except ImportError:
|
||||
values['slug'] = lambda self: self.id
|
||||
if isinstance(views_or_xmlid, basestring):
|
||||
if isinstance(views_or_xmlid, pycompat.string_types):
|
||||
views = self.env.ref(views_or_xmlid, raise_if_not_found=False)
|
||||
else:
|
||||
views = views_or_xmlid
|
||||
|
||||
@@ -58,7 +58,7 @@ class AuthorizeAPI():
|
||||
|
||||
:param etree._Element data: etree data to process
|
||||
"""
|
||||
data = etree.tostring(data, xml_declaration=True, encoding='utf-8')
|
||||
data = etree.tostring(data, encoding='utf-8')
|
||||
r = requests.post(self.url, data=data, headers={'Content-Type': 'text/xml'})
|
||||
r.raise_for_status()
|
||||
response = strip_ns(r.content, XMLNS)
|
||||
|
||||
@@ -95,7 +95,7 @@ class AuthorizeForm(AuthorizeCommon):
|
||||
if values[1] in ['submit', 'x_fp_hash', 'return_url', 'x_state', 'x_ship_to_state']:
|
||||
continue
|
||||
self.assertEqual(
|
||||
unicode(values[2], "utf-8"),
|
||||
values[2],
|
||||
form_values[values[1]],
|
||||
'Authorize: wrong value for input %s: received %s instead of %s' % (values[1], values[2], form_values[values[1]])
|
||||
)
|
||||
|
||||
@@ -158,7 +158,7 @@ class ProductProduct(models.Model):
|
||||
quantity = self._context.get('quantity', 1.0)
|
||||
|
||||
# Support context pricelists specified as display_name or ID for compatibility
|
||||
if isinstance(pricelist_id_or_name, basestring):
|
||||
if isinstance(pricelist_id_or_name, pycompat.string_types):
|
||||
pricelist_name_search = self.env['product.pricelist'].name_search(pricelist_id_or_name, operator='=', limit=1)
|
||||
if pricelist_name_search:
|
||||
pricelist = self.env['product.pricelist'].browse([pricelist_name_search[0][0]])
|
||||
|
||||
@@ -171,7 +171,7 @@ class ProductTemplate(models.Model):
|
||||
quantity = self._context.get('quantity', 1.0)
|
||||
|
||||
# Support context pricelists specified as display_name or ID for compatibility
|
||||
if isinstance(pricelist_id_or_name, basestring):
|
||||
if isinstance(pricelist_id_or_name, pycompat.string_types):
|
||||
pricelist_data = self.env['product.pricelist'].name_search(pricelist_id_or_name, operator='=', limit=1)
|
||||
if pricelist_data:
|
||||
pricelist = self.env['product.pricelist'].browse(pricelist_data[0][0])
|
||||
|
||||
@@ -161,7 +161,7 @@ class Product(models.Model):
|
||||
if self.env.context.get('location', False):
|
||||
if isinstance(self.env.context['location'], pycompat.integer_types):
|
||||
location_ids = [self.env.context['location']]
|
||||
elif isinstance(self.env.context['location'], basestring):
|
||||
elif isinstance(self.env.context['location'], pycompat.string_types):
|
||||
domain = [('complete_name', 'ilike', self.env.context['location'])]
|
||||
if self.env.context.get('force_company', False):
|
||||
domain += [('company_id', '=', self.env.context['force_company'])]
|
||||
@@ -172,7 +172,7 @@ class Product(models.Model):
|
||||
if self.env.context.get('warehouse', False):
|
||||
if isinstance(self.env.context['warehouse'], pycompat.integer_types):
|
||||
wids = [self.env.context['warehouse']]
|
||||
elif isinstance(self.env.context['warehouse'], basestring):
|
||||
elif isinstance(self.env.context['warehouse'], pycompat.string_types):
|
||||
domain = [('name', 'ilike', self.env.context['warehouse'])]
|
||||
if self.env.context.get('force_company', False):
|
||||
domain += [('company_id', '=', self.env.context['force_company'])]
|
||||
|
||||
@@ -174,7 +174,7 @@ class WebsiteSurvey(http.Controller):
|
||||
answer_tag = "%s_%s" % (answer_tag, 'comment')
|
||||
answer_value = answer.value_text
|
||||
elif answer.answer_type == 'number':
|
||||
answer_value = answer.value_number.__str__()
|
||||
answer_value = str(answer.value_number)
|
||||
elif answer.answer_type == 'date':
|
||||
answer_value = answer.value_date
|
||||
elif answer.answer_type == 'suggestion' and not answer.value_suggested_row:
|
||||
|
||||
@@ -8,6 +8,7 @@ from werkzeug import urls
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import pycompat
|
||||
|
||||
emails_split = re.compile(r"[;,\n\r]+")
|
||||
email_validator = re.compile(r"[^@]+@[^@]+\.[^@]+")
|
||||
@@ -119,7 +120,7 @@ class SurveyMailComposeMessage(models.TransientModel):
|
||||
if wizard.public != 'email_private':
|
||||
return None
|
||||
else:
|
||||
token = uuid.uuid4().__str__()
|
||||
token = pycompat.text_type(uuid.uuid4())
|
||||
# create response with token
|
||||
survey_user_input = SurveyUserInput.create({
|
||||
'survey_id': wizard.survey_id.id,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from odoo import api, fields, models, SUPERUSER_ID
|
||||
from odoo.http import request
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class UtmMedium(models.Model):
|
||||
@@ -72,7 +73,7 @@ class UtmMixin(models.AbstractModel):
|
||||
# ir_http dispatch saves the url params in a cookie
|
||||
value = request.httprequest.cookies.get(cookie_name)
|
||||
# if we receive a string for a many2one, we search/create the id
|
||||
if field.type == 'many2one' and isinstance(value, basestring) and value:
|
||||
if field.type == 'many2one' and isinstance(value, pycompat.string_types) and value:
|
||||
Model = self.env[field.comodel_name]
|
||||
records = Model.search([('name', '=', value)], limit=1)
|
||||
if not records:
|
||||
|
||||
@@ -1396,7 +1396,7 @@ class CSVExport(ExportFormat, http.Controller):
|
||||
for data in rows:
|
||||
row = []
|
||||
for d in data:
|
||||
if isinstance(d, unicode):
|
||||
if type(d) != str and isinstance(d, pycompat.string_types):
|
||||
try:
|
||||
d = d.encode('utf-8')
|
||||
except UnicodeError:
|
||||
@@ -1446,7 +1446,7 @@ class ExcelExport(ExportFormat, http.Controller):
|
||||
for row_index, row in enumerate(rows):
|
||||
for cell_index, cell_value in enumerate(row):
|
||||
cell_style = base_style
|
||||
if isinstance(cell_value, basestring):
|
||||
if isinstance(cell_value, pycompat.string_types):
|
||||
cell_value = re.sub("\r", " ", cell_value)
|
||||
elif isinstance(cell_value, datetime.datetime):
|
||||
cell_style = datetime_style
|
||||
|
||||
@@ -39,7 +39,7 @@ class Web_Editor(http.Controller):
|
||||
debug=request.debug)
|
||||
|
||||
for k in kwargs:
|
||||
if isinstance(kwargs[k], basestring) and kwargs[k].isdigit():
|
||||
if isinstance(kwargs[k], pycompat.string_types) and kwargs[k].isdigit():
|
||||
kwargs[k] = int(kwargs[k])
|
||||
|
||||
trans = dict(
|
||||
@@ -186,7 +186,7 @@ class Web_Editor(http.Controller):
|
||||
uploads += attachments.read(['name', 'mimetype', 'checksum', 'url'])
|
||||
except Exception as e:
|
||||
logger.exception("Failed to upload image to attachment")
|
||||
message = unicode(e)
|
||||
message = pycompat.text_type(e)
|
||||
|
||||
return """<script type='text/javascript'>
|
||||
window.parent['%s'](%s, %s);
|
||||
|
||||
@@ -48,7 +48,10 @@ class QWeb(models.AbstractModel):
|
||||
el.set('t-call', el.attrib.pop('t-snippet'))
|
||||
name = self.env['ir.ui.view'].search([('key', '=', el.attrib.get('t-call'))]).display_name
|
||||
thumbnail = el.attrib.pop('t-thumbnail', "oe-thumbnail")
|
||||
div = u'<div name="%s" data-oe-type="snippet" data-oe-thumbnail="%s">' % (escape(ir_qweb.unicodifier(name)), escape(ir_qweb.unicodifier(thumbnail)))
|
||||
div = u'<div name="%s" data-oe-type="snippet" data-oe-thumbnail="%s">' % (
|
||||
escape(pycompat.to_text(name)),
|
||||
escape(pycompat.to_text(thumbnail))
|
||||
)
|
||||
return [self._append(ast.Str(div))] + self._compile_node(el, options) + [self._append(ast.Str(u'</div>'))]
|
||||
|
||||
def _compile_directive_install(self, el, options):
|
||||
@@ -58,7 +61,11 @@ class QWeb(models.AbstractModel):
|
||||
return []
|
||||
name = el.attrib.get('string') or 'Snippet'
|
||||
thumbnail = el.attrib.pop('t-thumbnail', 'oe-thumbnail')
|
||||
div = u'<div name="%s" data-oe-type="snippet" data-module-id="%s" data-oe-thumbnail="%s"><section/></div>' % (escape(ir_qweb.unicodifier(name)), module.id, escape(ir_qweb.unicodifier(thumbnail)))
|
||||
div = u'<div name="%s" data-oe-type="snippet" data-module-id="%s" data-oe-thumbnail="%s"><section/></div>' % (
|
||||
escape(pycompat.to_text(name)),
|
||||
module.id,
|
||||
escape(pycompat.to_text(thumbnail))
|
||||
)
|
||||
return [self._append(ast.Str(div))]
|
||||
else:
|
||||
return []
|
||||
@@ -202,7 +209,7 @@ class DateTime(models.AbstractModel):
|
||||
def attributes(self, record, field_name, options, values):
|
||||
attrs = super(DateTime, self).attributes(record, field_name, options, values)
|
||||
value = record[field_name]
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, pycompat.string_types):
|
||||
value = fields.Datetime.from_string(value)
|
||||
if value:
|
||||
# convert from UTC (server timezone) to user timezone
|
||||
@@ -326,7 +333,7 @@ class Image(models.AbstractModel):
|
||||
|
||||
img = '<img class="%s" src="%s" style="%s"%s%s/>' % \
|
||||
(classes, src, options.get('style', ''), ' alt="%s"' % alt if alt else '', ' data-zoom="1" data-zoom-image="%s"' % src_zoom if src_zoom else '')
|
||||
return ir_qweb.unicodifier(img)
|
||||
return pycompat.to_text(img)
|
||||
|
||||
local_url_re = re.compile(r'^/(?P<module>[^]]+)/static/(?P<rest>.+)$')
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ class IrUiView(models.Model):
|
||||
|
||||
@api.model
|
||||
def _view_obj(self, view_id):
|
||||
if isinstance(view_id, basestring):
|
||||
if isinstance(view_id, pycompat.string_types):
|
||||
return self.env.ref(view_id)
|
||||
elif isinstance(view_id, pycompat.integer_types):
|
||||
return self.browse(view_id)
|
||||
|
||||
@@ -381,7 +381,7 @@ class Website(Home):
|
||||
action = action_id = None
|
||||
|
||||
# find the action_id: either an xml_id, the path, or an ID
|
||||
if isinstance(path_or_xml_id_or_id, basestring) and '.' in path_or_xml_id_or_id:
|
||||
if isinstance(path_or_xml_id_or_id, pycompat.string_types) and '.' in path_or_xml_id_or_id:
|
||||
action = request.env.ref(path_or_xml_id_or_id, raise_if_not_found=False)
|
||||
if not action:
|
||||
action = ServerActions.search([('website_path', '=', path_or_xml_id_or_id), ('website_published', '=', True)], limit=1)
|
||||
|
||||
@@ -52,7 +52,7 @@ class View(models.Model):
|
||||
|
||||
@api.model
|
||||
def _view_obj(self, view_id):
|
||||
if isinstance(view_id, basestring):
|
||||
if isinstance(view_id, pycompat.string_types):
|
||||
if 'website_id' in self._context:
|
||||
domain = [('key', '=', view_id), '|', ('website_id', '=', False), ('website_id', '=', self._context.get('website_id'))]
|
||||
order = 'website_id'
|
||||
|
||||
@@ -37,11 +37,8 @@ DEFAULT_CDN_FILTERS = [
|
||||
|
||||
|
||||
def url_for(path_or_uri, lang=None):
|
||||
if isinstance(path_or_uri, unicode):
|
||||
path_or_uri = path_or_uri.encode('utf-8')
|
||||
current_path = request.httprequest.path
|
||||
if isinstance(current_path, unicode):
|
||||
current_path = current_path.encode('utf-8')
|
||||
path_or_uri = pycompat.to_native(path_or_uri)
|
||||
current_path = pycompat.to_native(request.httprequest.path)
|
||||
location = path_or_uri.strip()
|
||||
force_lang = lang is not None
|
||||
url = urls.url_parse(location)
|
||||
@@ -667,7 +664,7 @@ class Menu(models.Model):
|
||||
self.browse(to_delete).unlink()
|
||||
for menu in data['data']:
|
||||
mid = menu['id']
|
||||
if isinstance(mid, basestring):
|
||||
if isinstance(mid, pycompat.string_types):
|
||||
new_menu = self.create({'name': menu['name']})
|
||||
replace_id(mid, new_menu.id)
|
||||
for menu in data['data']:
|
||||
|
||||
@@ -4,6 +4,8 @@ import os.path
|
||||
|
||||
from werkzeug import urls
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
"""
|
||||
* adds github_link(mode) context variable: provides URL (in relevant mode) of
|
||||
current document on github
|
||||
@@ -99,7 +101,7 @@ def add_doc_link(app, pagename, templatename, context, doctree):
|
||||
# in Sphinx 1.3 it's possible to have mutliple source suffixes and that
|
||||
# may be useful in the future
|
||||
source_suffix = app.config.source_suffix
|
||||
source_suffix = source_suffix if isinstance(source_suffix, basestring) else source_suffix[0]
|
||||
source_suffix = source_suffix if isinstance(source_suffix, pycompat.string_types) else source_suffix[0]
|
||||
# can't use functools.partial because 3rd positional is line not mode
|
||||
context['github_link'] = lambda mode='edit': make_github_link(
|
||||
app, 'doc/%s%s' % (pagename, source_suffix), mode=mode)
|
||||
|
||||
@@ -11,6 +11,8 @@ from docutils import nodes
|
||||
from sphinx import addnodes, util
|
||||
from sphinx.locale import admonitionlabels
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
def _parents(node):
|
||||
while node.parent:
|
||||
@@ -62,7 +64,7 @@ class BootstrapTranslator(nodes.NodeVisitor, object):
|
||||
self.param_separator = ','
|
||||
|
||||
def encode(self, text):
|
||||
return unicode(text).translate({
|
||||
return pycompat.text_type(text).translate({
|
||||
ord('&'): u'&',
|
||||
ord('<'): u'<',
|
||||
ord('"'): u'"',
|
||||
@@ -71,7 +73,7 @@ class BootstrapTranslator(nodes.NodeVisitor, object):
|
||||
})
|
||||
|
||||
def starttag(self, node, tagname, **attributes):
|
||||
tagname = unicode(tagname).lower()
|
||||
tagname = pycompat.text_type(tagname).lower()
|
||||
|
||||
# extract generic attributes
|
||||
attrs = {name.lower(): value for name, value in attributes.items()}
|
||||
@@ -105,8 +107,8 @@ class BootstrapTranslator(nodes.NodeVisitor, object):
|
||||
)
|
||||
# only "space characters" SPACE, CHARACTER TABULATION, LINE FEED,
|
||||
# FORM FEED and CARRIAGE RETURN should be collapsed, not al White_Space
|
||||
def attval(self, value, whitespace=re.compile(u'[ \t\n\f\r]')):
|
||||
return self.encode(whitespace.sub(u' ', unicode(value)))
|
||||
def attval(self, value, whitespace=re.compile(u'[ \t\n\f\r]+')):
|
||||
return self.encode(whitespace.sub(u' ', pycompat.text_type(value)))
|
||||
|
||||
def astext(self):
|
||||
return u''.join(self.body)
|
||||
|
||||
@@ -38,9 +38,9 @@ class IrFieldsConverter(models.AbstractModel):
|
||||
@api.model
|
||||
def _format_import_error(self, error_type, error_msg, error_params=(), error_args=None):
|
||||
# sanitize error params for later formatting by the import system
|
||||
sanitize = lambda p: p.replace('%', '%%') if isinstance(p, basestring) else p
|
||||
sanitize = lambda p: p.replace('%', '%%') if isinstance(p, pycompat.string_types) else p
|
||||
if error_params:
|
||||
if isinstance(error_params, basestring):
|
||||
if isinstance(error_params, pycompat.string_types):
|
||||
error_params = sanitize(error_params)
|
||||
elif isinstance(error_params, dict):
|
||||
error_params = {k: sanitize(v) for k, v in pycompat.items(error_params)}
|
||||
@@ -78,7 +78,7 @@ class IrFieldsConverter(models.AbstractModel):
|
||||
try:
|
||||
converted[field], ws = converters[field](value)
|
||||
for w in ws:
|
||||
if isinstance(w, basestring):
|
||||
if isinstance(w, pycompat.string_types):
|
||||
# wrap warning string in an ImportWarning for
|
||||
# uniform handling
|
||||
w = ImportWarning(w)
|
||||
@@ -269,14 +269,14 @@ class IrFieldsConverter(models.AbstractModel):
|
||||
for item, label in selection:
|
||||
label = ustr(label)
|
||||
labels = [label] + self._get_translations(('selection', 'model', 'code'), label)
|
||||
if value == unicode(item) or value in labels:
|
||||
if value == pycompat.text_type(item) or value in labels:
|
||||
return item, []
|
||||
|
||||
raise self._format_import_error(
|
||||
ValueError,
|
||||
_(u"Value '%s' not found in selection field '%%(field)s'"),
|
||||
value,
|
||||
{'moreinfo': [_label or unicode(item) for item, _label in selection if _label or item]}
|
||||
{'moreinfo': [_label or pycompat.text_type(item) for item, _label in selection if _label or item]}
|
||||
)
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -18,11 +18,6 @@ _logger = logging.getLogger(__name__)
|
||||
MODULE_UNINSTALL_FLAG = '_force_unlink'
|
||||
|
||||
|
||||
def encode(s):
|
||||
""" Return an UTF8-encoded version of ``s``. """
|
||||
return s.encode('utf8') if isinstance(s, unicode) else s
|
||||
|
||||
|
||||
# base environment for doing a safe_eval
|
||||
SAFE_EVAL_BASE = {
|
||||
'datetime': datetime,
|
||||
@@ -272,7 +267,7 @@ class IrModel(models.Model):
|
||||
def _instanciate(self, model_data):
|
||||
""" Return a class for the custom model given by parameters ``model_data``. """
|
||||
class CustomModel(models.Model):
|
||||
_name = encode(model_data['model'])
|
||||
_name = pycompat.to_native(model_data['model'])
|
||||
_description = model_data['name']
|
||||
_module = False
|
||||
_custom = True
|
||||
@@ -1134,7 +1129,7 @@ class IrModelAccess(models.Model):
|
||||
# User root have all accesses
|
||||
return True
|
||||
|
||||
assert isinstance(model, basestring), 'Not a model name: %s' % (model,)
|
||||
assert isinstance(model, pycompat.string_types), 'Not a model name: %s' % (model,)
|
||||
assert mode in ('read', 'write', 'create', 'unlink'), 'Invalid access mode'
|
||||
|
||||
# TransientModel records have no access rights, only an implicit access rule
|
||||
|
||||
@@ -5,5 +5,5 @@ from . import fields
|
||||
from . import assetsbundle
|
||||
|
||||
from .assetsbundle import AssetsBundle
|
||||
from .qweb import unicodifier, QWebException
|
||||
from .qweb import QWebException
|
||||
from .ir_qweb import IrQWeb, QWeb
|
||||
|
||||
@@ -6,8 +6,7 @@ from io import BytesIO
|
||||
from odoo import api, fields, models, _
|
||||
from PIL import Image
|
||||
import babel
|
||||
from odoo.tools import html_escape as escape, posix_to_ldml, safe_eval, float_utils, format_date
|
||||
from .qweb import unicodifier
|
||||
from odoo.tools import html_escape as escape, posix_to_ldml, safe_eval, float_utils, format_date, pycompat
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -20,7 +19,7 @@ def nl2br(string):
|
||||
:param str string:
|
||||
:rtype: unicode
|
||||
"""
|
||||
return unicodifier(string).replace(u'\n', u'<br>\n')
|
||||
return pycompat.to_text(string).replace(u'\n', u'<br>\n')
|
||||
|
||||
def html_escape(string, options):
|
||||
""" Automatically escapes content unless options['html-escape']
|
||||
@@ -89,7 +88,7 @@ class FieldConverter(models.AbstractModel):
|
||||
Converts a single value to its HTML version/output
|
||||
:rtype: unicode
|
||||
"""
|
||||
return html_escape(unicodifier(value) or u'', options)
|
||||
return html_escape(pycompat.to_text(value), options)
|
||||
|
||||
@api.model
|
||||
def record_to_html(self, record, field_name, options):
|
||||
@@ -124,7 +123,7 @@ class IntegerConverter(models.AbstractModel):
|
||||
|
||||
@api.model
|
||||
def value_to_html(self, value, options):
|
||||
return unicodifier(self.user_lang().format('%d', value, grouping=True).replace(r'-', u'\u2011'))
|
||||
return pycompat.to_text(self.user_lang().format('%d', value, grouping=True).replace(r'-', u'\u2011'))
|
||||
|
||||
|
||||
class FloatConverter(models.AbstractModel):
|
||||
@@ -153,7 +152,7 @@ class FloatConverter(models.AbstractModel):
|
||||
if precision is None:
|
||||
formatted = re.sub(r'(?:(0|\d+?)0+)$', r'\1', formatted)
|
||||
|
||||
return unicodifier(formatted)
|
||||
return pycompat.to_text(formatted)
|
||||
|
||||
@api.model
|
||||
def record_to_html(self, record, field_name, options):
|
||||
@@ -183,7 +182,7 @@ class DateTimeConverter(models.AbstractModel):
|
||||
lang = self.user_lang()
|
||||
locale = babel.Locale.parse(lang.code)
|
||||
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, pycompat.string_types):
|
||||
value = fields.Datetime.from_string(value)
|
||||
|
||||
value = fields.Datetime.context_timestamp(self, value)
|
||||
@@ -201,7 +200,7 @@ class DateTimeConverter(models.AbstractModel):
|
||||
if options and options.get('hide_seconds'):
|
||||
pattern = pattern.replace(":ss", "").replace(":s", "")
|
||||
|
||||
return unicodifier(babel.dates.format_datetime(value, format=pattern, locale=locale))
|
||||
return pycompat.to_text(babel.dates.format_datetime(value, format=pattern, locale=locale))
|
||||
|
||||
|
||||
class TextConverter(models.AbstractModel):
|
||||
@@ -224,7 +223,7 @@ class SelectionConverter(models.AbstractModel):
|
||||
def value_to_html(self, value, options):
|
||||
if not value:
|
||||
return ''
|
||||
return html_escape(unicodifier(options['selection'][value]) or u'', options)
|
||||
return html_escape(pycompat.to_text(options['selection'][value]) or u'', options)
|
||||
|
||||
@api.model
|
||||
def record_to_html(self, record, field_name, options):
|
||||
@@ -253,7 +252,7 @@ class HTMLConverter(models.AbstractModel):
|
||||
|
||||
@api.model
|
||||
def value_to_html(self, value, options):
|
||||
return unicodifier(value) or u''
|
||||
return pycompat.to_text(value)
|
||||
|
||||
|
||||
class ImageConverter(models.AbstractModel):
|
||||
@@ -278,7 +277,7 @@ class ImageConverter(models.AbstractModel):
|
||||
except: # image.verify() throws "suitable exceptions", I have no idea what they are
|
||||
raise ValueError("Invalid image content")
|
||||
|
||||
return unicodifier(b'<img src="data:%s;base64,%s">' % (Image.MIME[image.format].encode('ascii'), value))
|
||||
return u'<img src="data:%s;base64,%s">' % (Image.MIME[image.format], value.decode('ascii'))
|
||||
|
||||
|
||||
class MonetaryConverter(models.AbstractModel):
|
||||
@@ -399,13 +398,13 @@ class RelativeDatetimeConverter(models.AbstractModel):
|
||||
def value_to_html(self, value, options):
|
||||
locale = babel.Locale.parse(self.user_lang().code)
|
||||
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, pycompat.string_types):
|
||||
value = fields.Datetime.from_string(value)
|
||||
|
||||
# value should be a naive datetime in UTC. So is fields.Datetime.now()
|
||||
reference = fields.Datetime.from_string(options['now'])
|
||||
|
||||
return unicodifier(babel.dates.format_timedelta(value - reference, add_direction=True, locale=locale))
|
||||
return pycompat.to_text(babel.dates.format_timedelta(value - reference, add_direction=True, locale=locale))
|
||||
|
||||
@api.model
|
||||
def record_to_html(self, record, field_name, options):
|
||||
@@ -482,4 +481,4 @@ class QwebView(models.AbstractModel):
|
||||
|
||||
view = view.with_context(object=record)
|
||||
|
||||
return unicodifier(view.render(view._context, engine='ir.qweb'))
|
||||
return pycompat.to_text(view.render(view._context, engine='ir.qweb'))
|
||||
|
||||
@@ -218,7 +218,7 @@ class IrQWeb(models.AbstractModel, QWeb):
|
||||
files = []
|
||||
remains = []
|
||||
for el in html.fragments_fromstring(template):
|
||||
if isinstance(el, basestring):
|
||||
if isinstance(el, pycompat.string_types):
|
||||
remains.append(el)
|
||||
elif isinstance(el, html.HtmlElement):
|
||||
href = el.get('href', '')
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import ast
|
||||
import logging
|
||||
import os.path
|
||||
import re
|
||||
import traceback
|
||||
|
||||
@@ -176,13 +177,6 @@ class QWebException(Exception):
|
||||
# Avoid DeprecationWarning while still remaining compatible with werkzeug pre-0.9
|
||||
escape = (lambda text: _escape(text, quote=True)) if getattr(werkzeug, '__version__', '0.0') < '0.9.0' else _escape
|
||||
|
||||
def unicodifier(val):
|
||||
if val is None or val is False:
|
||||
return u''
|
||||
if isinstance(val, str):
|
||||
return val.decode('utf-8')
|
||||
return unicode(val)
|
||||
|
||||
def foreach_iterator(base_ctx, enum, name):
|
||||
ctx = base_ctx.copy()
|
||||
if not enum:
|
||||
@@ -380,10 +374,11 @@ class QWeb(object):
|
||||
if isinstance(document, etree._Element):
|
||||
element = document
|
||||
document = etree.tostring(document)
|
||||
elif document.startswith("<?xml"):
|
||||
element = etree.fromstring(document)
|
||||
else:
|
||||
elif os.path.exists(document):
|
||||
element = etree.parse(document).getroot()
|
||||
else:
|
||||
element = etree.fromstring(document)
|
||||
|
||||
for node in element:
|
||||
if node.get('t-name') == str(template):
|
||||
return (node, document)
|
||||
@@ -527,12 +522,14 @@ class QWeb(object):
|
||||
* itertools
|
||||
Define:
|
||||
* escape
|
||||
* unicodifier (empty string for a None or False, otherwise unicode string)
|
||||
* to_text (empty string for a None or False, otherwise unicode string)
|
||||
* string_types (replacement for basestring)
|
||||
"""
|
||||
return ast.parse(dedent("""
|
||||
from collections import OrderedDict
|
||||
from odoo.tools import pycompat
|
||||
from odoo.addons.base.ir.ir_qweb.qweb import escape, unicodifier, foreach_iterator
|
||||
from odoo.tools.pycompat import to_text, string_types
|
||||
from odoo.addons.base.ir.ir_qweb.qweb import escape, foreach_iterator
|
||||
"""))
|
||||
|
||||
def _create_def(self, options, body, prefix='fn', lineno=None):
|
||||
@@ -777,7 +774,7 @@ class QWeb(object):
|
||||
|
||||
if unqualified_el_tag == 't':
|
||||
return content
|
||||
tag = u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in pycompat.items(attrib)]))
|
||||
tag = u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(pycompat.to_text(value))) for name, value in pycompat.items(attrib)]))
|
||||
if unqualified_el_tag in self._void_elements:
|
||||
return [self._append(ast.Str(tag + '/>'))] + content
|
||||
else:
|
||||
@@ -879,7 +876,7 @@ class QWeb(object):
|
||||
# append(u' ')
|
||||
# append(name)
|
||||
# append(u'="')
|
||||
# append(escape(unicodifier((value)))
|
||||
# append(escape(to_text((value)))
|
||||
# append(u'"')
|
||||
body.append(ast.For(
|
||||
target=ast.Tuple(elts=[ast.Name(id='name', ctx=ast.Store()), ast.Name(id='value', ctx=ast.Store())], ctx=ast.Store()),
|
||||
@@ -901,7 +898,7 @@ class QWeb(object):
|
||||
func=ast.Name(id='isinstance', ctx=ast.Load()),
|
||||
args=[
|
||||
ast.Name(id='value', ctx=ast.Load()),
|
||||
ast.Name(id='basestring', ctx=ast.Load())
|
||||
ast.Name(id='string_types', ctx=ast.Load())
|
||||
],
|
||||
keywords=[],
|
||||
starargs=None, kwargs=None
|
||||
@@ -915,7 +912,7 @@ class QWeb(object):
|
||||
self._append(ast.Call(
|
||||
func=ast.Name(id='escape', ctx=ast.Load()),
|
||||
args=[ast.Call(
|
||||
func=ast.Name(id='unicodifier', ctx=ast.Load()),
|
||||
func=ast.Name(id='to_text', ctx=ast.Load()),
|
||||
args=[ast.Name(id='value', ctx=ast.Load())], keywords=[],
|
||||
starargs=None, kwargs=None
|
||||
)], keywords=[],
|
||||
@@ -955,7 +952,7 @@ class QWeb(object):
|
||||
if unqualified_el_tag == 't':
|
||||
return content
|
||||
|
||||
body = [self._append(ast.Str(u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in pycompat.items(extra_attrib)]))))]
|
||||
body = [self._append(ast.Str(u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(pycompat.to_text(value))) for name, value in pycompat.items(extra_attrib)]))))]
|
||||
body.extend(self._compile_all_attributes(el, options, attr_already_created))
|
||||
if unqualified_el_tag in self._void_elements:
|
||||
body.append(self._append(ast.Str(u'/>')))
|
||||
@@ -1044,7 +1041,7 @@ class QWeb(object):
|
||||
def _compile_directive_content(self, el, options):
|
||||
body = []
|
||||
if el.text is not None:
|
||||
body.append(self._append(ast.Str(unicodifier(el.text))))
|
||||
body.append(self._append(ast.Str(pycompat.to_text(el.text))))
|
||||
if el.getchildren():
|
||||
for item in el:
|
||||
# ignore comments & processing instructions
|
||||
@@ -1135,7 +1132,7 @@ class QWeb(object):
|
||||
)]
|
||||
|
||||
def _compile_tail(self, el):
|
||||
return el.tail is not None and [self._append(ast.Str(unicodifier(el.tail)))] or []
|
||||
return el.tail is not None and [self._append(ast.Str(pycompat.to_text(el.tail)))] or []
|
||||
|
||||
def _compile_directive_esc(self, el, options):
|
||||
field_options = self._compile_widget_options(el, 'esc')
|
||||
@@ -1148,14 +1145,14 @@ class QWeb(object):
|
||||
return content + self._compile_widget_value(el, options)
|
||||
|
||||
def _compile_widget(self, el, expression, field_options, escape=None):
|
||||
# if isinstance(value, basestring):
|
||||
# value = escape(unicodifier(value))
|
||||
# if isinstance(value, string_types):
|
||||
# value = escape(to_text(value))
|
||||
escaped = ast.If(
|
||||
test=ast.Call(
|
||||
func=ast.Name(id='isinstance', ctx=ast.Load()),
|
||||
args=[
|
||||
ast.Name(id='content', ctx=ast.Load()),
|
||||
ast.Name(id='basestring', ctx=ast.Load())
|
||||
ast.Name(id='string_types', ctx=ast.Load())
|
||||
], keywords=[],
|
||||
starargs=None, kwargs=None
|
||||
),
|
||||
@@ -1164,7 +1161,7 @@ class QWeb(object):
|
||||
value=ast.Call(
|
||||
func=ast.Name(id=escape, ctx=ast.Load()),
|
||||
args=[ast.Call(
|
||||
func=ast.Name(id='unicodifier', ctx=ast.Load()),
|
||||
func=ast.Name(id='to_text', ctx=ast.Load()),
|
||||
args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
|
||||
starargs=None, kwargs=None
|
||||
)], keywords=[],
|
||||
@@ -1332,7 +1329,7 @@ class QWeb(object):
|
||||
]
|
||||
|
||||
# if content is not None:
|
||||
# display the tag (unicodifier(content))
|
||||
# display the tag (to_text(content))
|
||||
# else
|
||||
# if default_content:
|
||||
# display the tag with default content
|
||||
@@ -1341,7 +1338,7 @@ class QWeb(object):
|
||||
return [self._if_content_is_not_Falsy(
|
||||
body=self._compile_tag(el, [self._append(
|
||||
ast.Call(
|
||||
func=ast.Name(id='unicodifier', ctx=ast.Load()),
|
||||
func=ast.Name(id='to_text', ctx=ast.Load()),
|
||||
args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
|
||||
starargs=None, kwargs=None
|
||||
)
|
||||
@@ -1461,7 +1458,7 @@ class QWeb(object):
|
||||
keys = []
|
||||
values = []
|
||||
for key, value in pycompat.items(options['nsmap']):
|
||||
if isinstance(key, basestring):
|
||||
if isinstance(key, pycompat.string_types):
|
||||
keys.append(ast.Str(s=key))
|
||||
elif key is None:
|
||||
keys.append(ast.Name(id='None', ctx=ast.Load()))
|
||||
@@ -1547,7 +1544,7 @@ class QWeb(object):
|
||||
def _compile_strexpr(self, expr):
|
||||
# ensure result is unicode
|
||||
return ast.Call(
|
||||
func=ast.Name(id='unicodifier', ctx=ast.Load()),
|
||||
func=ast.Name(id='to_text', ctx=ast.Load()),
|
||||
args=[self._compile_expr(expr)], keywords=[],
|
||||
starargs=None, kwargs=None
|
||||
)
|
||||
@@ -1591,7 +1588,7 @@ class QWeb(object):
|
||||
for m in _FORMAT_REGEX.finditer(f):
|
||||
literal = f[base_idx:m.start()]
|
||||
if literal:
|
||||
elts.append(ast.Str(literal if isinstance(literal, unicode) else literal.decode('utf-8')))
|
||||
elts.append(ast.Str(literal if isinstance(literal, pycompat.text_type) else literal.decode('utf-8')))
|
||||
|
||||
expr = m.group(1) or m.group(2)
|
||||
elts.append(self._compile_strexpr(expr))
|
||||
@@ -1599,7 +1596,7 @@ class QWeb(object):
|
||||
# string past last regex match
|
||||
literal = f[base_idx:]
|
||||
if literal:
|
||||
elts.append(ast.Str(literal if isinstance(literal, unicode) else literal.decode('utf-8')))
|
||||
elts.append(ast.Str(literal if isinstance(literal, pycompat.text_type) else literal.decode('utf-8')))
|
||||
|
||||
return reduce(lambda acc, it: ast.BinOp(
|
||||
left=acc,
|
||||
|
||||
@@ -394,7 +394,7 @@ class IrTranslation(models.Model):
|
||||
# always pass unicode so we can remove the string encoding/decoding.
|
||||
if not lang:
|
||||
return tools.ustr(source or '')
|
||||
if isinstance(types, basestring):
|
||||
if isinstance(types, pycompat.string_types):
|
||||
types = (types,)
|
||||
if res_id:
|
||||
if isinstance(res_id, pycompat.integer_types):
|
||||
|
||||
@@ -29,7 +29,7 @@ from odoo.tools.convert import _fix_multiple_roots
|
||||
from odoo.tools.parse_version import parse_version
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo.tools.view_validation import valid_view
|
||||
from odoo.tools.translate import encode, xml_translate, TRANSLATED_ATTRS
|
||||
from odoo.tools.translate import xml_translate, TRANSLATED_ATTRS
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -233,7 +233,7 @@ actual arch.
|
||||
else:
|
||||
_logger.warning("View %s: Full path [%s] cannot be found.", view.xml_id, view.arch_fs)
|
||||
arch_fs = False
|
||||
view.arch = arch_fs or view.arch_db
|
||||
view.arch = pycompat.to_text(arch_fs or view.arch_db)
|
||||
|
||||
def _inverse_arch(self):
|
||||
for view in self:
|
||||
@@ -269,7 +269,7 @@ actual arch.
|
||||
view.model_data_id = data['id']
|
||||
|
||||
def _search_model_data_id(self, operator, value):
|
||||
name = 'name' if isinstance(value, basestring) else 'id'
|
||||
name = 'name' if isinstance(value, pycompat.string_types) else 'id'
|
||||
domain = [('model', '=', 'ir.ui.view'), (name, operator, value)]
|
||||
data = self.env['ir.model.data'].sudo().search(domain)
|
||||
return [('id', 'in', data.mapped('res_id'))]
|
||||
@@ -313,7 +313,7 @@ actual arch.
|
||||
# Sanity checks: the view should not break anything upon rendering!
|
||||
# Any exception raised below will cause a transaction rollback.
|
||||
for view in self:
|
||||
view_arch = etree.fromstring(encode(view.arch))
|
||||
view_arch = etree.fromstring(view.arch.encode('utf-8'))
|
||||
view._valid_inheritance(view_arch)
|
||||
view_def = view.read_combined(['arch'])
|
||||
view_arch_utf8 = view_def['arch']
|
||||
@@ -942,7 +942,7 @@ actual arch.
|
||||
arch_tree = etree.fromstring(arch)
|
||||
self.distribute_branding(arch_tree)
|
||||
root = E.templates(arch_tree)
|
||||
arch = etree.tostring(root, encoding='utf-8', xml_declaration=True)
|
||||
arch = etree.tostring(root, encoding='utf-8')
|
||||
return arch
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -211,8 +211,8 @@ class IrValues(models.Model):
|
||||
match)
|
||||
:return: the newly created ir.values entry
|
||||
"""
|
||||
if isinstance(value, unicode):
|
||||
value = value.encode('utf8')
|
||||
if isinstance(value, pycompat.string_types):
|
||||
value = pycompat.to_native(value)
|
||||
if company_id is True:
|
||||
# should be company-specific, need to get company id
|
||||
company_id = self.env.user.company_id.id
|
||||
@@ -347,7 +347,7 @@ class IrValues(models.Model):
|
||||
specific record of the model, not all records.
|
||||
:return: the newly created ir.values entry
|
||||
"""
|
||||
assert isinstance(action, basestring) and ',' in action, \
|
||||
assert isinstance(action, pycompat.string_types) and ',' in action, \
|
||||
'Action definition must be an action reference, e.g. "ir.actions.act_window,42"'
|
||||
assert action_slot in ACTION_SLOTS, \
|
||||
'Action slot (%s) must be one of: %r' % (action_slot, ACTION_SLOTS)
|
||||
|
||||
@@ -645,7 +645,7 @@ class Module(models.Model):
|
||||
updated_values = {}
|
||||
for key in values:
|
||||
old = getattr(mod, key)
|
||||
updated = tools.ustr(values[key]) if isinstance(values[key], basestring) else values[key]
|
||||
updated = tools.ustr(values[key]) if isinstance(values[key], pycompat.string_types) else values[key]
|
||||
if (old or updated) and updated != old:
|
||||
updated_values[key] = values[key]
|
||||
if terp.get('installable', True) and mod.state == 'uninstallable':
|
||||
|
||||
@@ -107,7 +107,7 @@ class Groups(models.Model):
|
||||
return expression.AND(domains)
|
||||
else:
|
||||
return expression.OR(domains)
|
||||
if isinstance(operand, basestring):
|
||||
if isinstance(operand, pycompat.string_types):
|
||||
lst = False
|
||||
operand = [operand]
|
||||
where = []
|
||||
@@ -306,7 +306,7 @@ class Users(models.Model):
|
||||
|
||||
@api.model
|
||||
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
|
||||
groupby_fields = set([groupby] if isinstance(groupby, basestring) else groupby)
|
||||
groupby_fields = set([groupby] if isinstance(groupby, pycompat.string_types) else groupby)
|
||||
if groupby_fields.intersection(USER_PRIVATE_FIELDS):
|
||||
raise AccessError(_("Invalid 'group by' parameter"))
|
||||
return super(Users, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
|
||||
@@ -739,7 +739,7 @@ class GroupsView(models.Model):
|
||||
xml2.append({'class': "o_label_nowrap"})
|
||||
xml = E.field(E.group(*(xml1), col="2"), E.group(*(xml2), col="4"), name="groups_id", position="replace")
|
||||
xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS"))
|
||||
xml_content = etree.tostring(xml, pretty_print=True, xml_declaration=True, encoding="utf-8")
|
||||
xml_content = etree.tostring(xml, pretty_print=True, encoding="utf-8")
|
||||
view.with_context(lang=None).write({'arch': xml_content, 'arch_fs': False})
|
||||
|
||||
def get_application_groups(self, domain):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import functools
|
||||
import unittest
|
||||
|
||||
from odoo.tools import frozendict
|
||||
from odoo.tools import frozendict, pycompat
|
||||
from odoo.tools.func import compose
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class TestCompose(unittest.TestCase):
|
||||
def test_decorator(self):
|
||||
""" ensure compose() can be partially applied as a decorator
|
||||
"""
|
||||
@functools.partial(compose, unicode)
|
||||
@functools.partial(compose, pycompat.text_type)
|
||||
def mul(a, b):
|
||||
return a * b
|
||||
|
||||
|
||||
@@ -484,7 +484,7 @@ class FileSystemLoader(object):
|
||||
if node.get('t-name') == name:
|
||||
root = etree.Element('templates')
|
||||
root.append(deepcopy(node))
|
||||
arch = etree.tostring(root, encoding='utf-8', xml_declaration=True)
|
||||
arch = etree.tostring(root, encoding='utf-8')
|
||||
return arch
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class TestResConfig(TransactionCase):
|
||||
# Check types
|
||||
self.assertIsInstance(res, tuple)
|
||||
self.assertEqual(len(res), 2, "The result should contain 2 elements")
|
||||
self.assertIsInstance(res[0], basestring)
|
||||
self.assertIsInstance(res[0], pycompat.string_types)
|
||||
self.assertIsInstance(res[1], pycompat.integer_types)
|
||||
|
||||
# Check returned values
|
||||
@@ -58,7 +58,7 @@ class TestResConfig(TransactionCase):
|
||||
res = self.ResConfig.get_option_name(self.full_field_name)
|
||||
|
||||
# Check type
|
||||
self.assertIsInstance(res, basestring)
|
||||
self.assertIsInstance(res, pycompat.string_types)
|
||||
|
||||
# Check returned value
|
||||
self.assertEqual(res, self.expected_name)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
def selection_fn(model):
|
||||
@@ -48,7 +49,7 @@ for name, field in MODELS:
|
||||
|
||||
@api.model
|
||||
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||
if isinstance(name, basestring) and name.split(':')[0] == self._name:
|
||||
if isinstance(name, pycompat.string_types) and name.split(':')[0] == self._name:
|
||||
records = self.search([('value', operator, int(name.split(':')[1]))])
|
||||
return records.name_get()
|
||||
else:
|
||||
@@ -70,7 +71,7 @@ class One2ManyChild(models.Model):
|
||||
|
||||
@api.model
|
||||
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||
if isinstance(name, basestring) and name.split(':')[0] == self._name:
|
||||
if isinstance(name, pycompat.string_types) and name.split(':')[0] == self._name:
|
||||
records = self.search([('value', operator, int(name.split(':')[1]))])
|
||||
return records.name_get()
|
||||
else:
|
||||
@@ -124,7 +125,7 @@ class Many2ManyChild(models.Model):
|
||||
|
||||
@api.model
|
||||
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||
if isinstance(name, basestring) and name.split(':')[0] == self._name:
|
||||
if isinstance(name, pycompat.string_types) and name.split(':')[0] == self._name:
|
||||
records = self.search([('value', operator, int(name.split(':')[1]))])
|
||||
return records.name_get()
|
||||
else:
|
||||
|
||||
@@ -69,7 +69,7 @@ class test_integer_field(CreatorCase):
|
||||
def test_huge(self):
|
||||
self.assertEqual(
|
||||
self.export(2**31-1),
|
||||
[[unicode(2**31-1)]])
|
||||
[[pycompat.text_type(2**31-1)]])
|
||||
|
||||
|
||||
class test_float_field(CreatorCase):
|
||||
@@ -319,7 +319,7 @@ class test_m2o(CreatorCase):
|
||||
record = self.env['export.integer'].create({'value': 42})
|
||||
self.assertEqual(
|
||||
self.export(record.id, fields=['value/.id', 'value/value']),
|
||||
[[unicode(record.id), u'42']])
|
||||
[[pycompat.text_type(record.id), u'42']])
|
||||
|
||||
def test_external_id(self):
|
||||
record = self.env['export.integer'].create({'value': 42})
|
||||
|
||||
@@ -5,7 +5,7 @@ from datetime import date, datetime
|
||||
|
||||
from odoo.exceptions import AccessError, except_orm
|
||||
from odoo.tests import common
|
||||
from odoo.tools import mute_logger, float_repr
|
||||
from odoo.tools import mute_logger, float_repr, pycompat
|
||||
|
||||
|
||||
class TestFields(common.TransactionCase):
|
||||
@@ -16,8 +16,8 @@ class TestFields(common.TransactionCase):
|
||||
discussion = self.env.ref('test_new_api.discussion_0')
|
||||
|
||||
# read field as a record attribute or as a record item
|
||||
self.assertIsInstance(discussion.name, basestring)
|
||||
self.assertIsInstance(discussion['name'], basestring)
|
||||
self.assertIsInstance(discussion.name, pycompat.string_types)
|
||||
self.assertIsInstance(discussion['name'], pycompat.string_types)
|
||||
self.assertEqual(discussion['name'], discussion.name)
|
||||
|
||||
# read it with method read()
|
||||
|
||||
+17
-28
@@ -20,12 +20,11 @@ except ImportError:
|
||||
|
||||
import psycopg2
|
||||
|
||||
from odoo.sql_db import LazyCursor
|
||||
from odoo.tools import float_repr, float_round, frozendict, html_sanitize, human_size, pg_varchar, ustr, OrderedSet, pycompat
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
|
||||
from odoo.tools.translate import html_translate, _
|
||||
import odoo.tools.sql as sql
|
||||
from .sql_db import LazyCursor
|
||||
from .tools import float_repr, float_round, frozendict, html_sanitize, human_size, pg_varchar, ustr, OrderedSet, pycompat, sql
|
||||
from .tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
|
||||
from .tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
|
||||
from .tools.translate import html_translate, _
|
||||
|
||||
DATE_LENGTH = len(date.today().strftime(DATE_FORMAT))
|
||||
DATETIME_LENGTH = len(datetime.now().strftime(DATETIME_FORMAT))
|
||||
@@ -499,7 +498,7 @@ class Field(MetaField('DummyField', (object,), {})):
|
||||
def make_depends(deps):
|
||||
return tuple(deps(model) if callable(deps) else deps)
|
||||
|
||||
if isinstance(self.compute, basestring):
|
||||
if isinstance(self.compute, pycompat.string_types):
|
||||
# if the compute method has been overridden, concatenate all their _depends
|
||||
self.depends = ()
|
||||
for method in resolve_mro(model, self.compute, callable):
|
||||
@@ -518,7 +517,7 @@ class Field(MetaField('DummyField', (object,), {})):
|
||||
def _setup_related_full(self, model):
|
||||
""" Setup the attributes of a related field. """
|
||||
# fix the type of self.related if necessary
|
||||
if isinstance(self.related, basestring):
|
||||
if isinstance(self.related, pycompat.string_types):
|
||||
self.related = tuple(self.related.split('.'))
|
||||
|
||||
# determine the chain of fields, and make sure they are all set up
|
||||
@@ -746,9 +745,7 @@ class Field(MetaField('DummyField', (object,), {})):
|
||||
""" Convert ``value`` from the ``write`` format to the SQL format. """
|
||||
if value is None or value is False:
|
||||
return None
|
||||
if isinstance(value, unicode):
|
||||
return value.encode('utf8')
|
||||
return str(value)
|
||||
return pycompat.to_native(value)
|
||||
|
||||
def convert_to_cache(self, value, record, validate=True):
|
||||
""" Convert ``value`` to the cache format; ``value`` may come from an
|
||||
@@ -974,7 +971,7 @@ class Field(MetaField('DummyField', (object,), {})):
|
||||
for field in fields:
|
||||
for record in records:
|
||||
record._cache[field] = field.convert_to_cache(False, record, validate=False)
|
||||
if isinstance(self.compute, basestring):
|
||||
if isinstance(self.compute, pycompat.string_types):
|
||||
getattr(records, self.compute)()
|
||||
else:
|
||||
self.compute(records)
|
||||
@@ -1048,14 +1045,14 @@ class Field(MetaField('DummyField', (object,), {})):
|
||||
|
||||
def determine_inverse(self, records):
|
||||
""" Given the value of ``self`` on ``records``, inverse the computation. """
|
||||
if isinstance(self.inverse, basestring):
|
||||
if isinstance(self.inverse, pycompat.string_types):
|
||||
getattr(records, self.inverse)()
|
||||
else:
|
||||
self.inverse(records)
|
||||
|
||||
def determine_domain(self, records, operator, value):
|
||||
""" Return a domain representing a condition on ``self``. """
|
||||
if isinstance(self.search, basestring):
|
||||
if isinstance(self.search, pycompat.string_types):
|
||||
return getattr(records, self.search)(operator, value)
|
||||
else:
|
||||
return self.search(records, operator, value)
|
||||
@@ -1510,7 +1507,7 @@ class Date(Field):
|
||||
def convert_to_cache(self, value, record, validate=True):
|
||||
if not value:
|
||||
return False
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, pycompat.string_types):
|
||||
if validate:
|
||||
# force parsing for validation
|
||||
self.from_string(value)
|
||||
@@ -1580,7 +1577,7 @@ class Datetime(Field):
|
||||
def convert_to_cache(self, value, record, validate=True):
|
||||
if not value:
|
||||
return False
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, pycompat.string_types):
|
||||
if validate:
|
||||
# force parsing for validation
|
||||
self.from_string(value)
|
||||
@@ -1736,7 +1733,7 @@ class Selection(Field):
|
||||
translated according to context language
|
||||
"""
|
||||
selection = self.selection
|
||||
if isinstance(selection, basestring):
|
||||
if isinstance(selection, pycompat.string_types):
|
||||
return getattr(env[self.model_name], selection)()
|
||||
if callable(selection):
|
||||
return selection(env[self.model_name])
|
||||
@@ -1753,7 +1750,7 @@ class Selection(Field):
|
||||
def get_values(self, env):
|
||||
""" return a list of the possible values """
|
||||
selection = self.selection
|
||||
if isinstance(selection, basestring):
|
||||
if isinstance(selection, pycompat.string_types):
|
||||
selection = getattr(env[self.model_name], selection)()
|
||||
elif callable(selection):
|
||||
selection = selection(env[self.model_name])
|
||||
@@ -1777,14 +1774,6 @@ class Selection(Field):
|
||||
return item[1]
|
||||
return False
|
||||
|
||||
def convert_to_column(self, value, record, values=None):
|
||||
""" Convert ``value`` from the ``write`` format to the SQL format. """
|
||||
if value is None or value is False:
|
||||
return None
|
||||
if isinstance(value, unicode):
|
||||
return value.encode('utf8')
|
||||
return str(value)
|
||||
|
||||
|
||||
class Reference(Selection):
|
||||
type = 'reference'
|
||||
@@ -1802,7 +1791,7 @@ class Reference(Selection):
|
||||
if isinstance(value, BaseModel):
|
||||
if not validate or (value._name in self.get_values(record.env) and len(value) <= 1):
|
||||
return process(value._name, value.id) if value else False
|
||||
elif isinstance(value, basestring):
|
||||
elif isinstance(value, pycompat.string_types):
|
||||
res_model, res_id = value.split(',')
|
||||
if record.env[res_model].browse(int(res_id)).exists():
|
||||
return process(res_model, int(res_id))
|
||||
@@ -2122,7 +2111,7 @@ class _RelationalMulti(_Relational):
|
||||
self.depends += tuple(
|
||||
self.name + '.' + arg[0]
|
||||
for arg in self.domain
|
||||
if isinstance(arg, (tuple, list)) and isinstance(arg[0], basestring)
|
||||
if isinstance(arg, (tuple, list)) and isinstance(arg[0], pycompat.string_types)
|
||||
)
|
||||
|
||||
|
||||
|
||||
+9
-9
@@ -44,12 +44,12 @@ except ImportError:
|
||||
psutil = None
|
||||
|
||||
import odoo
|
||||
from odoo.service.server import memory_info
|
||||
from odoo.service import security, model as service_model
|
||||
from odoo.tools.func import lazy_property
|
||||
from odoo.tools import ustr, consteq, frozendict, pycompat, unique
|
||||
from .service.server import memory_info
|
||||
from .service import security, model as service_model
|
||||
from .tools.func import lazy_property
|
||||
from .tools import ustr, consteq, frozendict, pycompat, unique
|
||||
|
||||
from odoo.modules.module import module_manifest
|
||||
from .modules.module import module_manifest
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
rpc_request = logging.getLogger(__name__ + '.rpc.request')
|
||||
@@ -510,7 +510,7 @@ def route(route=None, **kw):
|
||||
if isinstance(response, Response) or f.routing_type == 'json':
|
||||
return response
|
||||
|
||||
if isinstance(response, basestring):
|
||||
if isinstance(response, pycompat.string_types):
|
||||
return Response(response)
|
||||
|
||||
if isinstance(response, werkzeug.exceptions.HTTPException):
|
||||
@@ -1386,7 +1386,7 @@ class Root(object):
|
||||
else:
|
||||
raise
|
||||
|
||||
if isinstance(result, basestring):
|
||||
if isinstance(result, pycompat.string_types):
|
||||
response = Response(result, mimetype='text/html')
|
||||
else:
|
||||
response = result
|
||||
@@ -1554,7 +1554,7 @@ def send_file(filepath_or_fp, mimetype=None, as_attachment=False, filename=None,
|
||||
|
||||
:param cache_timeout: the timeout in seconds for the headers.
|
||||
"""
|
||||
if isinstance(filepath_or_fp, (str, unicode)):
|
||||
if isinstance(filepath_or_fp, pycompat.string_types):
|
||||
if not filename:
|
||||
filename = os.path.basename(filepath_or_fp)
|
||||
file = open(filepath_or_fp, 'rb')
|
||||
@@ -1604,7 +1604,7 @@ def send_file(filepath_or_fp, mimetype=None, as_attachment=False, filename=None,
|
||||
mtime,
|
||||
size,
|
||||
adler32(
|
||||
filename.encode('utf-8') if isinstance(filename, unicode)
|
||||
filename.encode('utf-8') if isinstance(filename, pycompat.text_type)
|
||||
else filename
|
||||
) & 0xffffffff
|
||||
))
|
||||
|
||||
+11
-8
@@ -37,13 +37,16 @@ def get_encodings(hint_encoding='utf-8'):
|
||||
if prefenc:
|
||||
yield prefenc
|
||||
|
||||
# not using pycompat to avoid circular import: pycompat is in tools much of
|
||||
# which comes back to import loglevels
|
||||
text_type = type(u'')
|
||||
def ustr(value, hint_encoding='utf-8', errors='strict'):
|
||||
"""This method is similar to the builtin `unicode`, except
|
||||
that it may try multiple encodings to find one that works
|
||||
for decoding `value`, and defaults to 'utf-8' first.
|
||||
|
||||
:param: value: the value to convert
|
||||
:param: hint_encoding: an optional encoding that was detecte
|
||||
:param value: the value to convert
|
||||
:param hint_encoding: an optional encoding that was detecte
|
||||
upstream and should be tried first to decode ``value``.
|
||||
:param str errors: optional `errors` flag to pass to the unicode
|
||||
built-in to indicate how illegal character values should be
|
||||
@@ -60,24 +63,24 @@ def ustr(value, hint_encoding='utf-8', errors='strict'):
|
||||
# cases faster (isinstance/issubclass are significantly slower)
|
||||
ttype = type(value)
|
||||
|
||||
if ttype is unicode:
|
||||
if ttype is text_type:
|
||||
return value
|
||||
|
||||
# special short-circuit for str, as we still needs to support
|
||||
# str subclasses such as `odoo.tools.unquote`
|
||||
if ttype is str or issubclass(ttype, str):
|
||||
if ttype is bytes or issubclass(ttype, bytes):
|
||||
|
||||
# try hint_encoding first, avoids call to get_encoding()
|
||||
# for the most common case
|
||||
try:
|
||||
return unicode(value, hint_encoding, errors=errors)
|
||||
return value.decode(hint_encoding, errors=errors)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# rare: no luck with hint_encoding, attempt other ones
|
||||
for ln in get_encodings(hint_encoding):
|
||||
try:
|
||||
return unicode(value, ln, errors=errors)
|
||||
return value.decode(ln, errors=errors)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -86,7 +89,7 @@ def ustr(value, hint_encoding='utf-8', errors='strict'):
|
||||
|
||||
# fallback for non-string values
|
||||
try:
|
||||
return unicode(value)
|
||||
return text_type(value)
|
||||
except Exception:
|
||||
raise UnicodeError('unable to convert %r' % (value,))
|
||||
|
||||
@@ -95,6 +98,6 @@ def exception_to_unicode(e):
|
||||
if hasattr(e, 'args'):
|
||||
return "\n".join((ustr(a) for a in e.args))
|
||||
try:
|
||||
return unicode(e)
|
||||
return text_type(e)
|
||||
except Exception:
|
||||
return u"Unknown message"
|
||||
|
||||
+22
-24
@@ -173,7 +173,7 @@ class NewId(object):
|
||||
return False
|
||||
__nonzero__ = __bool__
|
||||
|
||||
IdType = pycompat.integer_types + (str, unicode, NewId)
|
||||
IdType = pycompat.integer_types + pycompat.string_types + (NewId,)
|
||||
|
||||
|
||||
# maximum number of prefetched records
|
||||
@@ -184,6 +184,7 @@ LOG_ACCESS_COLUMNS = ['create_uid', 'create_date', 'write_uid', 'write_date']
|
||||
MAGIC_COLUMNS = ['id'] + LOG_ACCESS_COLUMNS
|
||||
|
||||
|
||||
@pycompat.implements_to_string
|
||||
class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
""" Base class for Odoo models.
|
||||
|
||||
@@ -405,7 +406,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
|
||||
# determine inherited models
|
||||
parents = cls._inherit
|
||||
parents = [parents] if isinstance(parents, basestring) else (parents or [])
|
||||
parents = [parents] if isinstance(parents, pycompat.string_types) else (parents or [])
|
||||
|
||||
# determine the model's name
|
||||
name = cls._name or (len(parents) == 1 and parents[0]) or cls.__name__
|
||||
@@ -769,7 +770,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
# avoid broken transaction) and keep going
|
||||
cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
|
||||
except Exception as e:
|
||||
message = (_('Unknown error during import:') + ' %s: %s' % (type(e), unicode(e)))
|
||||
message = (_(u'Unknown error during import:') + u' %s: %s' % (type(e), e))
|
||||
moreinfo = _('Resolve other errors first')
|
||||
messages.append(dict(info, type='error', message=message, moreinfo=moreinfo))
|
||||
# Failed for some reason, perhaps due to invalid data supplied,
|
||||
@@ -886,7 +887,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
# processing of response, but injects human readable in message
|
||||
exc_vals = dict(base, record=record, field=field_names[field])
|
||||
record = dict(base, type=type, record=record, field=field,
|
||||
message=unicode(exception.args[0]) % exc_vals)
|
||||
message=pycompat.text_type(exception.args[0]) % exc_vals)
|
||||
if len(exception.args) > 1 and exception.args[1]:
|
||||
record.update(exception.args[1])
|
||||
log(record)
|
||||
@@ -1714,7 +1715,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
value = False if value is None else value
|
||||
gb = groupby_dict.get(key)
|
||||
if gb and gb['type'] in ('date', 'datetime') and value:
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, pycompat.string_types):
|
||||
dt_format = DEFAULT_SERVER_DATETIME_FORMAT if gb['type'] == 'datetime' else DEFAULT_SERVER_DATE_FORMAT
|
||||
value = datetime.datetime.strptime(value, dt_format)
|
||||
if gb['tz_convert']:
|
||||
@@ -1821,7 +1822,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
"""
|
||||
result = self._read_group_raw(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
|
||||
|
||||
groupby = [groupby] if isinstance(groupby, basestring) else list(OrderedSet(groupby))
|
||||
groupby = [groupby] if isinstance(groupby, pycompat.string_types) else list(OrderedSet(groupby))
|
||||
dt = [
|
||||
f for f in groupby
|
||||
if self._fields[f.split(':')[0]].type in ('date', 'datetime')
|
||||
@@ -1845,7 +1846,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
query = self._where_calc(domain)
|
||||
fields = fields or [f.name for f in pycompat.values(self._fields) if f.store]
|
||||
|
||||
groupby = [groupby] if isinstance(groupby, basestring) else list(OrderedSet(groupby))
|
||||
groupby = [groupby] if isinstance(groupby, pycompat.string_types) else list(OrderedSet(groupby))
|
||||
groupby_list = groupby[:1] if lazy else groupby
|
||||
annotated_groupbys = [self._read_group_process_groupby(gb, query) for gb in groupby_list]
|
||||
groupby_fields = [g['field'] for g in annotated_groupbys]
|
||||
@@ -4396,7 +4397,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
"""
|
||||
if not func:
|
||||
return self # support for an empty path of fields
|
||||
if isinstance(func, basestring):
|
||||
if isinstance(func, pycompat.string_types):
|
||||
recs = self
|
||||
for name in func.split('.'):
|
||||
recs = recs._mapped_func(operator.itemgetter(name))
|
||||
@@ -4421,7 +4422,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
|
||||
:param func: a function or a dot-separated sequence of field names
|
||||
"""
|
||||
if isinstance(func, basestring):
|
||||
if isinstance(func, pycompat.string_types):
|
||||
name = func
|
||||
func = lambda rec: any(rec.mapped(name))
|
||||
return self.browse([rec.id for rec in self if func(rec)])
|
||||
@@ -4438,7 +4439,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
if key is None:
|
||||
recs = self.search([('id', 'in', self.ids)])
|
||||
return self.browse(reversed(recs._ids)) if reverse else recs
|
||||
if isinstance(key, basestring):
|
||||
if isinstance(key, pycompat.string_types):
|
||||
key = itemgetter(key)
|
||||
return self.browse(item.id for item in sorted(self, key=key, reverse=reverse))
|
||||
|
||||
@@ -4523,7 +4524,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
"""
|
||||
if isinstance(item, BaseModel) and self._name == item._name:
|
||||
return len(item) == 1 and item.id in self._ids
|
||||
elif isinstance(item, basestring):
|
||||
elif isinstance(item, pycompat.string_types):
|
||||
return item in self._fields
|
||||
else:
|
||||
raise TypeError("Mixing apples and oranges: %s in %s" % (item, self))
|
||||
@@ -4616,11 +4617,8 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
|
||||
def __str__(self):
|
||||
return "%s%s" % (self._name, getattr(self, '_ids', ""))
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(str(self))
|
||||
|
||||
__repr__ = __str__
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def __hash__(self):
|
||||
if hasattr(self, '_ids'):
|
||||
@@ -4640,7 +4638,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
rs = inst[10:20] # subset of inst
|
||||
nm = rs['name'] # name of first record in inst
|
||||
"""
|
||||
if isinstance(key, basestring):
|
||||
if isinstance(key, pycompat.string_types):
|
||||
# important: one must call the field's getter
|
||||
return self._fields[key].__get__(self, type(self))
|
||||
elif isinstance(key, slice):
|
||||
@@ -4983,7 +4981,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
|
||||
# At the moment, the client does not support updates on a *2many field
|
||||
# while this one is modified by the user.
|
||||
if isinstance(field_name, basestring) and \
|
||||
if isinstance(field_name, pycompat.string_types) and \
|
||||
self._fields[field_name].type in ('one2many', 'many2many'):
|
||||
dirty.discard(field_name)
|
||||
|
||||
@@ -5008,13 +5006,13 @@ class RecordCache(MutableMapping):
|
||||
|
||||
def contains(self, field):
|
||||
""" Return whether `records[0]` has a value for ``field`` in cache. """
|
||||
if isinstance(field, basestring):
|
||||
if isinstance(field, pycompat.string_types):
|
||||
field = self._recs._fields[field]
|
||||
return self._recs.id in self._recs.env.cache[field]
|
||||
|
||||
def __contains__(self, field):
|
||||
""" Return whether `records[0]` has a regular value for ``field`` in cache. """
|
||||
if isinstance(field, basestring):
|
||||
if isinstance(field, pycompat.string_types):
|
||||
field = self._recs._fields[field]
|
||||
dummy = SpecialValue(None)
|
||||
value = self._recs.env.cache[field].get(self._recs.id, dummy)
|
||||
@@ -5022,7 +5020,7 @@ class RecordCache(MutableMapping):
|
||||
|
||||
def get(self, field, default=None):
|
||||
""" Return the cached, regular value of ``field`` for `records[0]`, or ``default``. """
|
||||
if isinstance(field, basestring):
|
||||
if isinstance(field, pycompat.string_types):
|
||||
field = self._recs._fields[field]
|
||||
dummy = SpecialValue(None)
|
||||
value = self._recs.env.cache[field].get(self._recs.id, dummy)
|
||||
@@ -5030,14 +5028,14 @@ class RecordCache(MutableMapping):
|
||||
|
||||
def __getitem__(self, field):
|
||||
""" Return the cached value of ``field`` for `records[0]`. """
|
||||
if isinstance(field, basestring):
|
||||
if isinstance(field, pycompat.string_types):
|
||||
field = self._recs._fields[field]
|
||||
value = self._recs.env.cache[field][self._recs.id]
|
||||
return value.get() if isinstance(value, SpecialValue) else value
|
||||
|
||||
def __setitem__(self, field, value):
|
||||
""" Assign the cached value of ``field`` for all records in ``records``. """
|
||||
if isinstance(field, basestring):
|
||||
if isinstance(field, pycompat.string_types):
|
||||
field = self._recs._fields[field]
|
||||
values = dict.fromkeys(self._recs._ids, value)
|
||||
self._recs.env.cache[field].update(values)
|
||||
@@ -5056,7 +5054,7 @@ class RecordCache(MutableMapping):
|
||||
|
||||
def __delitem__(self, field):
|
||||
""" Remove the cached value of ``field`` for all ``records``. """
|
||||
if isinstance(field, basestring):
|
||||
if isinstance(field, pycompat.string_types):
|
||||
field = self._recs._fields[field]
|
||||
field_cache = self._recs.env.cache[field]
|
||||
for id in self._recs._ids:
|
||||
|
||||
+12
-17
@@ -377,7 +377,7 @@ def normalize_leaf(element):
|
||||
|
||||
def is_operator(element):
|
||||
""" Test whether an object is a valid domain operator. """
|
||||
return isinstance(element, basestring) and element in DOMAIN_OPERATORS
|
||||
return isinstance(element, pycompat.string_types) and element in DOMAIN_OPERATORS
|
||||
|
||||
|
||||
def is_leaf(element, internal=False):
|
||||
@@ -398,7 +398,7 @@ def is_leaf(element, internal=False):
|
||||
return (isinstance(element, tuple) or isinstance(element, list)) \
|
||||
and len(element) == 3 \
|
||||
and element[1] in INTERNAL_OPS \
|
||||
and ((isinstance(element[0], basestring) and element[0])
|
||||
and ((isinstance(element[0], pycompat.string_types) and element[0])
|
||||
or tuple(element) in (TRUE_LEAF, FALSE_LEAF))
|
||||
|
||||
|
||||
@@ -697,9 +697,9 @@ class expression(object):
|
||||
return the list of related ids
|
||||
"""
|
||||
names = []
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, pycompat.string_types):
|
||||
names = [value]
|
||||
elif value and isinstance(value, (tuple, list)) and all(isinstance(item, basestring) for item in value):
|
||||
elif value and isinstance(value, (tuple, list)) and all(isinstance(item, pycompat.string_types) for item in value):
|
||||
names = value
|
||||
elif isinstance(value, pycompat.integer_types):
|
||||
return [value]
|
||||
@@ -925,7 +925,7 @@ class expression(object):
|
||||
domain = domain(model)
|
||||
is_integer_m2o = comodel._fields[field.inverse_name].type == 'integer'
|
||||
if right is not False:
|
||||
if isinstance(right, basestring):
|
||||
if isinstance(right, pycompat.string_types):
|
||||
op = {'!=': '=', 'not like': 'like', 'not ilike': 'ilike'}.get(operator, operator)
|
||||
ids2 = [x[0] for x in comodel.name_search(right, domain or [], op, limit=None)]
|
||||
if ids2:
|
||||
@@ -993,7 +993,7 @@ class expression(object):
|
||||
else:
|
||||
call_null_m2m = True
|
||||
if right is not False:
|
||||
if isinstance(right, basestring):
|
||||
if isinstance(right, pycompat.string_types):
|
||||
op = {'!=': '=', 'not like': 'like', 'not ilike': 'ilike'}.get(operator, operator)
|
||||
domain = field.domain
|
||||
if callable(domain):
|
||||
@@ -1051,8 +1051,8 @@ class expression(object):
|
||||
res_ids.append(False) # TODO this should not be appended if False was in 'right'
|
||||
return left, 'in', res_ids
|
||||
# resolve string-based m2o criterion into IDs
|
||||
if isinstance(right, basestring) or \
|
||||
right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right):
|
||||
if isinstance(right, pycompat.string_types) or \
|
||||
right and isinstance(right, (tuple, list)) and all(isinstance(item, pycompat.string_types) for item in right):
|
||||
push(create_substitution_leaf(leaf, _get_expression(comodel, left, right, operator), model))
|
||||
else:
|
||||
# right == [] or right == False and all other cases are handled by __leaf_to_sql()
|
||||
@@ -1263,21 +1263,16 @@ class expression(object):
|
||||
|
||||
add_null = False
|
||||
if need_wildcard:
|
||||
if isinstance(right, str):
|
||||
str_utf8 = right
|
||||
elif isinstance(right, unicode):
|
||||
str_utf8 = right.encode('utf-8')
|
||||
else:
|
||||
str_utf8 = str(right)
|
||||
params = '%%%s%%' % str_utf8
|
||||
add_null = not str_utf8
|
||||
native_str = pycompat.to_native(right)
|
||||
params = '%%%s%%' % native_str
|
||||
add_null = not native_str
|
||||
elif left in model:
|
||||
params = model._fields[left].convert_to_column(right, model)
|
||||
|
||||
if add_null:
|
||||
query = '(%s OR %s."%s" IS NULL)' % (query, table_alias, left)
|
||||
|
||||
if isinstance(params, basestring):
|
||||
if isinstance(params, pycompat.string_types):
|
||||
params = [params]
|
||||
return query, params
|
||||
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ def setup_modifiers(node, field=None, context=None, in_tree_view=False):
|
||||
|
||||
def test_modifiers(what, expected):
|
||||
modifiers = {}
|
||||
if isinstance(what, basestring):
|
||||
if isinstance(what, pycompat.string_types):
|
||||
node = etree.fromstring(what)
|
||||
transfer_node_to_modifiers(node, modifiers)
|
||||
simplify_modifiers(modifiers)
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ import odoo.sql_db
|
||||
import odoo.tools
|
||||
from odoo.sql_db import db_connect
|
||||
from odoo.release import version_info
|
||||
from odoo.tools import pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -223,7 +224,7 @@ def exp_restore(db_name, data, copy=False):
|
||||
return True
|
||||
|
||||
def restore_db(db, dump_file, copy=False):
|
||||
assert isinstance(db, basestring)
|
||||
assert isinstance(db, pycompat.string_types)
|
||||
if exp_db_exist(db):
|
||||
_logger.info('RESTORE DB: %s already exists', db)
|
||||
raise Exception("Database already exists")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import copy
|
||||
from . import pycompat
|
||||
from . import win32
|
||||
from . import appdirs
|
||||
from .config import config
|
||||
|
||||
@@ -13,6 +13,7 @@ from __future__ import print_function
|
||||
# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
|
||||
# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
|
||||
# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
from . import pycompat
|
||||
|
||||
__version_info__ = (1, 3, 0)
|
||||
__version__ = '.'.join(str(v) for v in __version_info__)
|
||||
@@ -21,12 +22,6 @@ __version__ = '.'.join(str(v) for v in __version_info__)
|
||||
import sys
|
||||
import os
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
|
||||
def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
|
||||
r"""Return full path to the user-specific data dir for this application.
|
||||
@@ -397,7 +392,7 @@ def _get_win_folder_with_pywin32(csidl_name):
|
||||
# not return unicode strings when there is unicode data in the
|
||||
# path.
|
||||
try:
|
||||
dir = unicode(dir)
|
||||
dir = pycompat.text_type(dir)
|
||||
|
||||
# Downgrade to short path name if have highbit chars. See
|
||||
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ from decorator import decorator
|
||||
from inspect import formatargspec, getargspec
|
||||
import logging
|
||||
|
||||
from odoo.tools import pycompat
|
||||
from . import pycompat
|
||||
|
||||
unsafe_eval = eval
|
||||
|
||||
@@ -135,7 +135,7 @@ class ormcache_multi(ormcache):
|
||||
def determine_key(self):
|
||||
""" Determine the function that computes a cache key from arguments. """
|
||||
assert self.skiparg is None, "ormcache_multi() no longer supports skiparg"
|
||||
assert isinstance(self.multi, basestring), "ormcache_multi() parameter multi must be an argument name"
|
||||
assert isinstance(self.multi, pycompat.string_types), "ormcache_multi() parameter multi must be an argument name"
|
||||
|
||||
super(ormcache_multi, self).determine_key()
|
||||
|
||||
|
||||
+9
-11
@@ -5,15 +5,13 @@ try:
|
||||
import configparser as ConfigParser
|
||||
except ImportError:
|
||||
import ConfigParser
|
||||
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import odoo
|
||||
import odoo.conf
|
||||
import odoo.loglevels as loglevels
|
||||
import logging
|
||||
import odoo.release as release
|
||||
from .. import release, conf, loglevels
|
||||
from . import appdirs, pycompat
|
||||
|
||||
|
||||
@@ -398,10 +396,10 @@ class configmanager(object):
|
||||
if getattr(opt, arg):
|
||||
self.options[arg] = getattr(opt, arg)
|
||||
# ... or keep, but cast, the config file value.
|
||||
elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
|
||||
elif isinstance(self.options[arg], pycompat.string_types) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
|
||||
self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
|
||||
|
||||
if isinstance(self.options['log_handler'], basestring):
|
||||
if isinstance(self.options['log_handler'], pycompat.string_types):
|
||||
self.options['log_handler'] = self.options['log_handler'].split(',')
|
||||
self.options['log_handler'].extend(opt.log_handler)
|
||||
|
||||
@@ -433,7 +431,7 @@ class configmanager(object):
|
||||
if getattr(opt, arg) is not None:
|
||||
self.options[arg] = getattr(opt, arg)
|
||||
# ... or keep, but cast, the config file value.
|
||||
elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
|
||||
elif isinstance(self.options[arg], pycompat.string_types) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
|
||||
self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
|
||||
|
||||
self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.join(os.path.dirname(__file__), '..'))))
|
||||
@@ -471,9 +469,9 @@ class configmanager(object):
|
||||
if opt.save:
|
||||
self.save()
|
||||
|
||||
odoo.conf.addons_paths = self.options['addons_path'].split(',')
|
||||
conf.addons_paths = self.options['addons_path'].split(',')
|
||||
|
||||
odoo.conf.server_wide_modules = [
|
||||
conf.server_wide_modules = [
|
||||
m.strip() for m in self.options['server_wide_modules'].split(',') if m.strip()
|
||||
]
|
||||
|
||||
@@ -575,7 +573,7 @@ class configmanager(object):
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.options[key] = value
|
||||
if key in self.options and isinstance(self.options[key], basestring) and \
|
||||
if key in self.options and isinstance(self.options[key], pycompat.string_types) and \
|
||||
key in self.casts and self.casts[key].type in optparse.Option.TYPE_CHECKER:
|
||||
self.options[key] = optparse.Option.TYPE_CHECKER[self.casts[key].type](self.casts[key], key, self.options[key])
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import pytz
|
||||
from lxml import etree, builder
|
||||
|
||||
import odoo
|
||||
import odoo.release
|
||||
from . import assertion_report, pycompat
|
||||
from .config import config
|
||||
from .misc import file_open, unquote, ustr, SKIPPED_ELEMENT_TYPES
|
||||
@@ -683,7 +682,7 @@ form: module.record_id""" % (xml_id,)
|
||||
f_val = int(f_val)
|
||||
elif model._fields[f_name].type in ['float', 'monetary']:
|
||||
f_val = float(f_val)
|
||||
elif model._fields[f_name].type == 'boolean' and isinstance(f_val, basestring):
|
||||
elif model._fields[f_name].type == 'boolean' and isinstance(f_val, pycompat.string_types):
|
||||
f_val = str2bool(f_val)
|
||||
res[f_name] = f_val
|
||||
|
||||
@@ -893,7 +892,7 @@ def convert_xml_import(cr, module, xmlfile, idref=None, mode='init', noupdate=Fa
|
||||
|
||||
if idref is None:
|
||||
idref={}
|
||||
if isinstance(xmlfile, basestring):
|
||||
if isinstance(xmlfile, pycompat.string_types):
|
||||
xml_filename = xmlfile
|
||||
else:
|
||||
xml_filename = xmlfile.name
|
||||
|
||||
+4
-4
@@ -465,7 +465,7 @@ def human_size(sz):
|
||||
if not sz:
|
||||
return False
|
||||
units = ('bytes', 'Kb', 'Mb', 'Gb')
|
||||
if isinstance(sz,basestring):
|
||||
if isinstance(sz,pycompat.string_types):
|
||||
sz=len(sz)
|
||||
s, i = float(sz), 0
|
||||
while s >= 1024 and i < len(units)-1:
|
||||
@@ -798,7 +798,7 @@ class mute_logger(object):
|
||||
|
||||
def __enter__(self):
|
||||
for logger in self.loggers:
|
||||
assert isinstance(logger, basestring),\
|
||||
assert isinstance(logger, pycompat.string_types),\
|
||||
"A logger name must be a string, got %s" % type(logger)
|
||||
logging.getLogger(logger).addFilter(self)
|
||||
|
||||
@@ -1073,7 +1073,7 @@ def formatLang(env, value, digits=None, grouping=True, monetary=False, dp=False,
|
||||
if not digits and digits is not 0:
|
||||
digits = DEFAULT_DIGITS
|
||||
|
||||
if isinstance(value, (str, unicode)) and not value:
|
||||
if isinstance(value, pycompat.string_types) and not value:
|
||||
return ''
|
||||
|
||||
lang = env.user.company_id.partner_id.lang or 'en_US'
|
||||
@@ -1108,7 +1108,7 @@ def format_date(env, value, lang_code=False, date_format=False):
|
||||
return ''
|
||||
if isinstance(value, datetime.datetime):
|
||||
value = value.date()
|
||||
elif isinstance(value, basestring):
|
||||
elif isinstance(value, pycompat.string_types):
|
||||
if len(value) < DATE_LENGTH:
|
||||
return ''
|
||||
value = value[:DATE_LENGTH]
|
||||
|
||||
@@ -10,6 +10,15 @@ if PY2:
|
||||
unichr = unichr
|
||||
text_type = unicode
|
||||
string_types = (str, unicode)
|
||||
def to_native(source, encoding='utf-8', falsy_empty=False):
|
||||
if not source and falsy_empty:
|
||||
return ''
|
||||
|
||||
if isinstance(source, text_type):
|
||||
return source.encode(encoding)
|
||||
|
||||
return str(source)
|
||||
|
||||
integer_types = (int, long)
|
||||
|
||||
keys = lambda d: iter(d.iterkeys())
|
||||
@@ -19,6 +28,11 @@ if PY2:
|
||||
# noinspection PyUnresolvedReferences
|
||||
from itertools import imap, izip, ifilter
|
||||
|
||||
def implements_to_string(cls):
|
||||
cls.__unicode__ = cls.__str__
|
||||
cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
|
||||
return cls
|
||||
|
||||
def implements_iterator(cls):
|
||||
cls.next = cls.__next__
|
||||
del cls.__next__
|
||||
@@ -30,6 +44,15 @@ else:
|
||||
unichr = chr
|
||||
text_type = str
|
||||
string_types = (str,)
|
||||
def to_native(source, encoding='utf-8', falsy_empty=False):
|
||||
if not source and falsy_empty:
|
||||
return ''
|
||||
|
||||
if isinstance(source, bytes):
|
||||
return source.decode(encoding)
|
||||
|
||||
return str(source)
|
||||
|
||||
integer_types = (int,)
|
||||
|
||||
keys = lambda d: iter(d.keys())
|
||||
@@ -40,6 +63,9 @@ else:
|
||||
izip = zip
|
||||
ifilter = filter
|
||||
|
||||
def implements_to_string(cls):
|
||||
return cls
|
||||
|
||||
def implements_iterator(cls):
|
||||
return cls
|
||||
|
||||
@@ -47,3 +73,20 @@ else:
|
||||
if value.__traceback__ != tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
def to_text(source):
|
||||
""" Generates a text value (an instance of text_type) from an arbitrary
|
||||
source.
|
||||
|
||||
* False and None are converted to empty strings
|
||||
* text is passed through
|
||||
* bytes are decoded as UTF-8
|
||||
* rest is textified via the current version's relevant data model method
|
||||
"""
|
||||
if source is None or source is False:
|
||||
return u''
|
||||
|
||||
if isinstance(source, bytes):
|
||||
return source.decode('utf-8')
|
||||
|
||||
return text_type(source)
|
||||
|
||||
@@ -26,6 +26,7 @@ import werkzeug
|
||||
|
||||
from . import pycompat
|
||||
from .misc import ustr
|
||||
from . import pycompat
|
||||
|
||||
import odoo
|
||||
|
||||
@@ -267,8 +268,9 @@ _BUILTINS = {
|
||||
'True': True,
|
||||
'False': False,
|
||||
'None': None,
|
||||
'bytes': bytes,
|
||||
'str': str,
|
||||
'unicode': unicode,
|
||||
'unicode': pycompat.text_type,
|
||||
'bool': bool,
|
||||
'int': int,
|
||||
'float': float,
|
||||
|
||||
+14
-15
@@ -7,15 +7,14 @@
|
||||
through the code of yaml tests.
|
||||
"""
|
||||
|
||||
import odoo
|
||||
import odoo.tools as tools
|
||||
import logging
|
||||
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from subprocess import Popen, PIPE
|
||||
import os
|
||||
import tempfile
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from .. import api
|
||||
from . import pycompat, ustr, config
|
||||
from .safe_eval import safe_eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
_test_logger = logging.getLogger('odoo.tests')
|
||||
@@ -30,7 +29,7 @@ def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None, re
|
||||
context = {}
|
||||
_test_logger.info(" - Trying %s.create(%r)", rname, ids)
|
||||
|
||||
env = odoo.api.Environment(cr, uid, context)
|
||||
env = api.Environment(cr, uid, context)
|
||||
|
||||
report_id = env['ir.actions.report'].search([('report_name', '=', rname)], limit=1)
|
||||
if not report_id:
|
||||
@@ -41,12 +40,12 @@ def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None, re
|
||||
if not res_data:
|
||||
raise ValueError("Report %s produced an empty result!" % rname)
|
||||
|
||||
if tools.config['test_report_directory']:
|
||||
open(os.path.join(tools.config['test_report_directory'], rname+ '.'+res_format), 'wb+').write(res_data)
|
||||
if config['test_report_directory']:
|
||||
open(os.path.join(config['test_report_directory'], rname+ '.'+res_format), 'wb+').write(res_data)
|
||||
|
||||
_logger.debug("Have a %s report for %s, will examine it", res_format, rname)
|
||||
if res_format == 'pdf':
|
||||
if res_data[:5] != '%PDF-':
|
||||
if res_data[:5] != b'%PDF-':
|
||||
raise ValueError("Report %s produced a non-pdf header, %r" % (rname, res_data[:10]))
|
||||
res_text = False
|
||||
try:
|
||||
@@ -56,7 +55,7 @@ def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None, re
|
||||
|
||||
proc = Popen(['pdftotext', '-enc', 'UTF-8', '-nopgbrk', rfname, '-'], shell=False, stdout=PIPE)
|
||||
stdout, stderr = proc.communicate()
|
||||
res_text = tools.ustr(stdout)
|
||||
res_text = ustr(stdout)
|
||||
os.unlink(rfname)
|
||||
except Exception:
|
||||
_logger.debug("Unable to parse PDF report: install pdftotext to perform automated tests.")
|
||||
@@ -92,14 +91,14 @@ def try_report_action(cr, uid, action_id, active_model=None, active_ids=None,
|
||||
Eg. 'OK' or 'fa-print'
|
||||
:param our_module: the name of the calling module (string), like 'account'
|
||||
"""
|
||||
if not our_module and isinstance(action_id, basestring):
|
||||
if not our_module and isinstance(action_id, pycompat.string_types):
|
||||
if '.' in action_id:
|
||||
our_module = action_id.split('.', 1)[0]
|
||||
|
||||
context = dict(context or {})
|
||||
# TODO context fill-up
|
||||
|
||||
env = odoo.api.Environment(cr, uid, context)
|
||||
env = api.Environment(cr, uid, context)
|
||||
|
||||
def log_test(msg, *args):
|
||||
_test_logger.info(" - " + msg, *args)
|
||||
@@ -113,7 +112,7 @@ def try_report_action(cr, uid, action_id, active_model=None, active_ids=None,
|
||||
if not wiz_buttons:
|
||||
wiz_buttons = []
|
||||
|
||||
if isinstance(action_id, basestring):
|
||||
if isinstance(action_id, pycompat.string_types):
|
||||
if '.' in action_id:
|
||||
_, act_xmlid = action_id.split('.', 1)
|
||||
else:
|
||||
@@ -138,7 +137,7 @@ def try_report_action(cr, uid, action_id, active_model=None, active_ids=None,
|
||||
if datas.get('id',False):
|
||||
context.update( {'active_id': datas.get('id',False), 'active_ids': datas.get('ids',[]), 'active_model': datas.get('model',False)})
|
||||
context1 = action.get('context', {})
|
||||
if isinstance(context1, basestring):
|
||||
if isinstance(context1, pycompat.string_types):
|
||||
context1 = safe_eval(context1, dict(context))
|
||||
context.update(context1)
|
||||
env = env(context=context)
|
||||
|
||||
+14
-17
@@ -127,9 +127,7 @@ class UNIX_LINE_TERMINATOR(csv.excel):
|
||||
csv.register_dialect("UNIX", UNIX_LINE_TERMINATOR)
|
||||
|
||||
|
||||
#
|
||||
# Helper functions for translating fields
|
||||
#
|
||||
# FIXME: holy shit this whole thing needs to be cleaned up hard it's a mess
|
||||
def encode(s):
|
||||
if isinstance(s, unicode):
|
||||
return s.encode('utf8')
|
||||
@@ -280,18 +278,18 @@ def translate_xml_node(node, callback, parse, serialize):
|
||||
|
||||
|
||||
def parse_xml(text):
|
||||
return etree.fromstring(encode(text))
|
||||
return etree.fromstring(text)
|
||||
|
||||
def serialize_xml(node):
|
||||
return etree.tostring(node, method='xml', encoding='utf8').decode('utf8')
|
||||
return etree.tostring(node, method='xml', encoding='unicode')
|
||||
|
||||
_HTML_PARSER = etree.HTMLParser(encoding='utf8')
|
||||
|
||||
def parse_html(text):
|
||||
return html.fragment_fromstring(encode(text), parser=_HTML_PARSER)
|
||||
return html.fragment_fromstring(text, parser=_HTML_PARSER)
|
||||
|
||||
def serialize_html(node):
|
||||
return etree.tostring(node, method='html', encoding='utf8').decode('utf8')
|
||||
return etree.tostring(node, method='html', encoding='unicode')
|
||||
|
||||
|
||||
def xml_translate(callback, value):
|
||||
@@ -307,7 +305,7 @@ def xml_translate(callback, value):
|
||||
return serialize_xml(result)
|
||||
except etree.ParseError:
|
||||
# fallback for translated terms: use an HTML parser and wrap the term
|
||||
root = parse_html("<div>%s</div>" % value)
|
||||
root = parse_html(u"<div>%s</div>" % value)
|
||||
result = translate_xml_node(root, callback, parse_xml, serialize_xml)
|
||||
# remove tags <div> and </div> from result
|
||||
return serialize_xml(result)[5:-6]
|
||||
@@ -643,14 +641,13 @@ class PoFile(object):
|
||||
# only strings in python code are python formated
|
||||
self.buffer.write(u"#, python-format\n")
|
||||
|
||||
if not isinstance(trad, unicode):
|
||||
trad = unicode(trad, 'utf8')
|
||||
if not isinstance(source, unicode):
|
||||
source = unicode(source, 'utf8')
|
||||
|
||||
msg = u"msgid %s\n" \
|
||||
u"msgstr %s\n\n" \
|
||||
% (quote(source), quote(trad))
|
||||
msg = (
|
||||
u"msgid %s\n"
|
||||
u"msgstr %s\n\n"
|
||||
) % (
|
||||
quote(pycompat.text_type(source)),
|
||||
quote(pycompat.text_type(trad))
|
||||
)
|
||||
self.buffer.write(msg)
|
||||
|
||||
|
||||
@@ -1109,7 +1106,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True,
|
||||
return
|
||||
|
||||
if isinstance(res_id, pycompat.integer_types) or \
|
||||
(isinstance(res_id, basestring) and res_id.isdigit()):
|
||||
(isinstance(res_id, pycompat.string_types) and res_id.isdigit()):
|
||||
dic['res_id'] = int(res_id)
|
||||
if module_name:
|
||||
dic['module'] = module_name
|
||||
|
||||
@@ -12,8 +12,7 @@ from lxml import etree
|
||||
import yaml
|
||||
|
||||
import odoo
|
||||
from . import assertion_report
|
||||
from . import yaml_tag, pycompat
|
||||
from . import assertion_report, pycompat, yaml_tag
|
||||
from .config import config
|
||||
from .misc import file_open, DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from odoo import SUPERUSER_ID
|
||||
@@ -25,9 +24,6 @@ from .safe_eval import safe_eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def encode(s):
|
||||
return s.encode('utf8') if isinstance(s, unicode) else s
|
||||
|
||||
class YamlImportException(Exception):
|
||||
pass
|
||||
|
||||
@@ -41,7 +37,7 @@ def _is_yaml_mapping(node, tag_constructor):
|
||||
return value
|
||||
|
||||
def is_comment(node):
|
||||
return isinstance(node, basestring)
|
||||
return isinstance(node, pycompat.string_types)
|
||||
|
||||
def is_assert(node):
|
||||
return isinstance(node, yaml_tag.Assert) \
|
||||
@@ -87,7 +83,7 @@ def is_ir_set(node):
|
||||
return _is_yaml_mapping(node, yaml_tag.IrSet)
|
||||
|
||||
def is_string(node):
|
||||
return isinstance(node, basestring)
|
||||
return isinstance(node, pycompat.string_types)
|
||||
|
||||
class RecordDictWrapper(dict):
|
||||
"""
|
||||
@@ -278,7 +274,7 @@ class YamlInterpreter(object):
|
||||
record, fields = next(pycompat.items(node))
|
||||
model = self.env[record.model]
|
||||
view_id = record.view
|
||||
if view_id and (view_id is not True) and isinstance(view_id, basestring):
|
||||
if view_id and (view_id is not True) and isinstance(view_id, pycompat.string_types):
|
||||
if '.' not in view_id:
|
||||
view_id = self.module + '.' + view_id
|
||||
view_id = self.env.ref(view_id).id
|
||||
@@ -358,7 +354,10 @@ class YamlInterpreter(object):
|
||||
traverse(child, elems)
|
||||
|
||||
elems = OrderedDict()
|
||||
traverse(etree.fromstring(encode(view['arch'])), elems)
|
||||
arch = view['arch']
|
||||
if isinstance(arch, pycompat.text_type):
|
||||
arch = arch.encode('utf-8')
|
||||
traverse(etree.fromstring(arch), elems)
|
||||
return elems
|
||||
|
||||
def is_readonly(field_elem):
|
||||
@@ -584,7 +583,7 @@ class YamlInterpreter(object):
|
||||
assert python.model or python.id, "!python node must have attribute `model` or `id`"
|
||||
if python.id is None:
|
||||
record = self.env[python.model]
|
||||
elif isinstance(python.id, basestring):
|
||||
elif isinstance(python.id, pycompat.string_types):
|
||||
record = self.get_record(python.id)
|
||||
else:
|
||||
record = self.env[python.model].browse(python.id)
|
||||
|
||||
Reference in New Issue
Block a user