[IMP] *: replace type where possible
do not compare types, for exact checks use `is` / `is not`, for instance checks use `isinstance()`Flake8(E721) Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
This commit is contained in:
@@ -746,7 +746,7 @@ class account_journal(models.Model):
|
||||
action = self.env["ir.actions.act_window"]._for_xml_id(action_name)
|
||||
|
||||
context = self._context.copy()
|
||||
if 'context' in action and type(action['context']) == str:
|
||||
if 'context' in action and isinstance(action['context'], str):
|
||||
context.update(ast.literal_eval(action['context']))
|
||||
else:
|
||||
context.update(action.get('context', {}))
|
||||
|
||||
@@ -61,7 +61,7 @@ class AccountChartTemplate(models.AbstractModel):
|
||||
def is_template(func):
|
||||
return callable(func) and hasattr(func, '_l10n_template')
|
||||
template_register = defaultdict(lambda: defaultdict(list))
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
for _attr, func in getmembers(cls, is_template):
|
||||
template, model = func._l10n_template
|
||||
template_register[template][model].append(func)
|
||||
@@ -70,7 +70,7 @@ class AccountChartTemplate(models.AbstractModel):
|
||||
|
||||
def _setup_complete(self):
|
||||
super()._setup_complete()
|
||||
type(self)._template_register = AccountChartTemplate._template_register
|
||||
self.env.registry[self._name]._template_register = AccountChartTemplate._template_register
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
@@ -373,7 +373,7 @@ class ResPartner(models.Model):
|
||||
def _asset_difference_search(self, account_type, operator, operand):
|
||||
if operator not in ('<', '=', '>', '>=', '<='):
|
||||
return []
|
||||
if type(operand) not in (float, int):
|
||||
if not isinstance(operand, (float, int)):
|
||||
return []
|
||||
sign = 1
|
||||
if account_type == 'liability_payable':
|
||||
|
||||
@@ -816,7 +816,7 @@ class BaseAutomation(models.Model):
|
||||
""" Patch method `name` on `model`, unless it has been patched already. """
|
||||
if model not in patched_models[name]:
|
||||
patched_models[name].add(model)
|
||||
ModelClass = type(model)
|
||||
ModelClass = model.env.registry[model._name]
|
||||
method.origin = getattr(ModelClass, name)
|
||||
setattr(ModelClass, name, method)
|
||||
|
||||
|
||||
+10
-10
@@ -237,7 +237,7 @@ class Websocket:
|
||||
self._close_received = False
|
||||
self._timeout_manager = TimeoutManager()
|
||||
# Used for rate limiting.
|
||||
self._incoming_frame_timestamps = deque(maxlen=type(self).RL_BURST)
|
||||
self._incoming_frame_timestamps = deque(maxlen=self.RL_BURST)
|
||||
# Used to notify the websocket that bus notifications are
|
||||
# available.
|
||||
self.__notif_sock_w, self.__notif_sock_r = socket.socketpair()
|
||||
@@ -264,7 +264,7 @@ class Websocket:
|
||||
try:
|
||||
readables = {
|
||||
selector_key[0].fileobj for selector_key in
|
||||
self.__selector.select(type(self).INACTIVITY_TIMEOUT)
|
||||
self.__selector.select(self.INACTIVITY_TIMEOUT)
|
||||
}
|
||||
if self._timeout_manager.has_timed_out() and self.state is ConnectionState.OPEN:
|
||||
self.disconnect(
|
||||
@@ -405,7 +405,7 @@ class Websocket:
|
||||
payload_length = struct.unpack('!H', recv_bytes(2))[0]
|
||||
elif payload_length == 127:
|
||||
payload_length = struct.unpack('!Q', recv_bytes(8))[0]
|
||||
if payload_length > type(self).MESSAGE_MAX_SIZE:
|
||||
if payload_length > self.MESSAGE_MAX_SIZE:
|
||||
raise PayloadTooLargeException()
|
||||
|
||||
mask = recv_bytes(4)
|
||||
@@ -454,7 +454,7 @@ class Websocket:
|
||||
if frame.opcode is not Opcode.CONTINUE:
|
||||
raise ProtocolError("A continuation frame was expected")
|
||||
message_fragments.extend(frame.payload)
|
||||
if len(message_fragments) > type(self).MESSAGE_MAX_SIZE:
|
||||
if len(message_fragments) > self.MESSAGE_MAX_SIZE:
|
||||
raise PayloadTooLargeException()
|
||||
if frame.fin:
|
||||
return bytes(message_fragments)
|
||||
@@ -599,9 +599,9 @@ class Websocket:
|
||||
`RateLimitExceededException`.
|
||||
"""
|
||||
now = time.time()
|
||||
if len(self._incoming_frame_timestamps) >= type(self).RL_BURST:
|
||||
if len(self._incoming_frame_timestamps) >= self.RL_BURST:
|
||||
elapsed_time = now - self._incoming_frame_timestamps[0]
|
||||
if elapsed_time < type(self).RL_DELAY * type(self).RL_BURST:
|
||||
if elapsed_time < self.RL_DELAY * self.RL_BURST:
|
||||
raise RateLimitExceededException()
|
||||
self._incoming_frame_timestamps.append(now)
|
||||
|
||||
@@ -611,11 +611,11 @@ class Websocket:
|
||||
registered for this event type. Every callback is given both the
|
||||
environment and the related websocket.
|
||||
"""
|
||||
if not type(self).__event_callbacks[event_type]:
|
||||
if not self.__event_callbacks[event_type]:
|
||||
return
|
||||
with closing(acquire_cursor(self._db)) as cr:
|
||||
env = api.Environment(cr, self._session.uid, self._session.context)
|
||||
for callback in type(self).__event_callbacks[event_type]:
|
||||
for callback in self.__event_callbacks[event_type]:
|
||||
try:
|
||||
service_model.retrying(functools.partial(callback, env, self), env)
|
||||
except Exception:
|
||||
@@ -674,7 +674,7 @@ class TimeoutManager:
|
||||
# Custom keep alive timeout for each TimeoutManager to avoid multiple
|
||||
# connections timing out at the same time.
|
||||
self._keep_alive_timeout = (
|
||||
type(self).KEEP_ALIVE_TIMEOUT + random.uniform(0, type(self).KEEP_ALIVE_TIMEOUT / 2)
|
||||
self.KEEP_ALIVE_TIMEOUT + random.uniform(0, self.KEEP_ALIVE_TIMEOUT / 2)
|
||||
)
|
||||
self.timeout_reason = None
|
||||
# Start time recorded when we started awaiting an answer to a
|
||||
@@ -711,7 +711,7 @@ class TimeoutManager:
|
||||
if now - self._opened_at >= self._keep_alive_timeout:
|
||||
self.timeout_reason = TimeoutReason.KEEP_ALIVE
|
||||
return True
|
||||
if self._awaited_opcode and now - self._waiting_start_time >= type(self).TIMEOUT:
|
||||
if self._awaited_opcode and now - self._waiting_start_time >= self.TIMEOUT:
|
||||
self.timeout_reason = TimeoutReason.NO_RESPONSE
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -59,7 +59,7 @@ class EventBooth(models.Model):
|
||||
return [('state', '=', 'available')]
|
||||
|
||||
def _group_expand_states(self, states, domain, order):
|
||||
return [key for key, val in type(self).state.selection]
|
||||
return [key for key, val in self._fields['state'].selection]
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
|
||||
@@ -72,4 +72,4 @@ class FleetVehicleLogServices(models.Model):
|
||||
service.purchaser_id = service.vehicle_id.driver_id
|
||||
|
||||
def _expand_states(self, states, domain, order):
|
||||
return [key for key, dummy in type(self).state.selection]
|
||||
return [key for key, dummy in self._fields['state'].selection]
|
||||
|
||||
@@ -28,7 +28,7 @@ class GoogleAuth(http.Controller):
|
||||
)
|
||||
service_field = f'google_{service}_account_id'
|
||||
if service_field in request.env.user:
|
||||
getattr(request.env.user, service_field)._set_auth_tokens(access_token, refresh_token, ttl)
|
||||
request.env.user[service_field]._set_auth_tokens(access_token, refresh_token, ttl)
|
||||
else:
|
||||
raise Warning('No callback field for service <%s>' % service)
|
||||
return request.redirect(url_return)
|
||||
|
||||
@@ -42,7 +42,7 @@ class GoogleGmailController(http.Controller):
|
||||
|
||||
model = request.env[model_name]
|
||||
|
||||
if not issubclass(type(model), request.env.registry['google.gmail.mixin']):
|
||||
if not isinstance(model, request.env.registry['google.gmail.mixin']):
|
||||
# The model must inherits from the "google.gmail.mixin" mixin
|
||||
raise Forbidden()
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class Contract(models.Model):
|
||||
contract.calendar_mismatch = contract.resource_calendar_id != contract.employee_id.resource_calendar_id
|
||||
|
||||
def _expand_states(self, states, domain, order):
|
||||
return [key for key, val in type(self).state.selection]
|
||||
return [key for key, val in self._fields['state'].selection]
|
||||
|
||||
@api.depends('employee_id')
|
||||
def _compute_employee_contract(self):
|
||||
|
||||
@@ -100,7 +100,7 @@ class LinkTracker(models.Model):
|
||||
utms = {}
|
||||
for key, field_name, cook in self.env['utm.mixin'].tracking_fields():
|
||||
field = self._fields[field_name]
|
||||
attr = getattr(tracker, field_name)
|
||||
attr = tracker[field_name]
|
||||
if field.type == 'many2one':
|
||||
attr = attr.name
|
||||
if attr:
|
||||
|
||||
@@ -92,9 +92,9 @@ class IrModel(models.Model):
|
||||
|
||||
def _reflect_model_params(self, model):
|
||||
vals = super(IrModel, self)._reflect_model_params(model)
|
||||
vals['is_mail_thread'] = issubclass(type(model), self.pool['mail.thread'])
|
||||
vals['is_mail_activity'] = issubclass(type(model), self.pool['mail.activity.mixin'])
|
||||
vals['is_mail_blacklist'] = issubclass(type(model), self.pool['mail.thread.blacklist'])
|
||||
vals['is_mail_thread'] = isinstance(model, self.pool['mail.thread'])
|
||||
vals['is_mail_activity'] = isinstance(model, self.pool['mail.activity.mixin'])
|
||||
vals['is_mail_blacklist'] = isinstance(model, self.pool['mail.thread.blacklist'])
|
||||
return vals
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -35,9 +35,9 @@ class MailMail(models.Model):
|
||||
def default_get(self, fields):
|
||||
# protection for `default_type` values leaking from menu action context (e.g. for invoices)
|
||||
# To remove when automatic context propagation is removed in web client
|
||||
if self._context.get('default_type') not in type(self).message_type.base_field.selection:
|
||||
if self._context.get('default_type') not in self._fields['message_type'].base_field.selection:
|
||||
self = self.with_context(dict(self._context, default_type=None))
|
||||
if self._context.get('default_state') not in type(self).state.base_field.selection:
|
||||
if self._context.get('default_state') not in self._fields['state'].base_field.selection:
|
||||
self = self.with_context(dict(self._context, default_state='outgoing'))
|
||||
return super(MailMail, self).default_get(fields)
|
||||
|
||||
@@ -173,9 +173,8 @@ class MailMail(models.Model):
|
||||
SQL queries.
|
||||
"""
|
||||
super()._add_inherited_fields()
|
||||
cls = type(self)
|
||||
for field in ('email_from', 'reply_to', 'subject'):
|
||||
cls._fields[field].related_sudo = True
|
||||
self._fields[field].related_sudo = True
|
||||
|
||||
def action_retry(self):
|
||||
self.filtered(lambda mail: mail.state == 'exception').mark_outgoing()
|
||||
|
||||
@@ -108,7 +108,7 @@ class MailNotification(models.Model):
|
||||
def format_failure_reason(self):
|
||||
self.ensure_one()
|
||||
if self.failure_type != 'unknown':
|
||||
return dict(type(self).failure_type.selection).get(self.failure_type, _('No Error'))
|
||||
return dict(self._fields['failure_type'].selection).get(self.failure_type, _('No Error'))
|
||||
else:
|
||||
return _("Unknown error") + ": %s" % (self.failure_reason or '')
|
||||
|
||||
|
||||
@@ -763,7 +763,7 @@ class MailThread(models.AbstractModel):
|
||||
bounced_record_done = bounced_record_done or (bounced_record and model.model == bounced_model and bounced_record in rec_bounce_w_email)
|
||||
|
||||
# set record as bounced unless already done due to blacklist mixin
|
||||
if bounced_record and not bounced_record_done and issubclass(type(bounced_record), self.pool['mail.thread']):
|
||||
if bounced_record and not bounced_record_done and isinstance(bounced_record, self.pool['mail.thread']):
|
||||
bounced_record._message_receive_bounce(bounced_email, bounced_partner)
|
||||
|
||||
if bounced_partner and bounced_message:
|
||||
@@ -1931,7 +1931,7 @@ class MailThread(models.AbstractModel):
|
||||
If no partner has been found and/or created for a given emails its
|
||||
matching partner is an empty record.
|
||||
"""
|
||||
if records and issubclass(type(records), self.pool['mail.thread']):
|
||||
if records and isinstance(records, self.pool['mail.thread']):
|
||||
followers = records.mapped('message_partner_ids')
|
||||
else:
|
||||
followers = self.env['res.partner']
|
||||
|
||||
@@ -1271,7 +1271,7 @@ class MailComposer(models.TransientModel):
|
||||
blacklist = {x[0] for x in self._cr.fetchall()}
|
||||
if not blacklist:
|
||||
return blacklisted_rec_ids
|
||||
if issubclass(type(self.env[self.model]), self.pool['mail.thread.blacklist']):
|
||||
if isinstance(self.env[self.model], self.pool['mail.thread.blacklist']):
|
||||
targets = self.env[self.model].browse(mail_values_dict.keys())
|
||||
targets.fetch(['email_normalized'])
|
||||
# First extract email from recipient before comparing with blacklist
|
||||
|
||||
@@ -82,7 +82,7 @@ class MailThread(models.AbstractModel):
|
||||
"""
|
||||
defaults = {}
|
||||
|
||||
if issubclass(type(self), self.pool['utm.mixin']):
|
||||
if isinstance(self, self.pool['utm.mixin']):
|
||||
thread_references = msg_dict.get('references', '') or msg_dict.get('in_reply_to', '')
|
||||
msg_references = tools.mail_header_msgid_re.findall(thread_references)
|
||||
if msg_references:
|
||||
|
||||
@@ -559,7 +559,7 @@ class MassMailing(models.Model):
|
||||
return super(MassMailing, self).copy(default=default)
|
||||
|
||||
def _group_expand_states(self, states, domain, order):
|
||||
return [key for key, val in type(self).state.selection]
|
||||
return [key for key, val in self._fields['state'].selection]
|
||||
|
||||
# ------------------------------------------------------
|
||||
# ACTIONS
|
||||
|
||||
@@ -169,7 +169,7 @@ class Mailing(models.Model):
|
||||
target = self.env[self.mailing_model_real]
|
||||
|
||||
partner_fields = []
|
||||
if issubclass(type(target), self.pool['mail.thread.phone']):
|
||||
if isinstance(target, self.pool['mail.thread.phone']):
|
||||
phone_fields = ['phone_sanitized']
|
||||
else:
|
||||
phone_fields = [
|
||||
|
||||
@@ -46,7 +46,7 @@ class MicrosoftOutlookController(http.Controller):
|
||||
|
||||
model = request.env[model_name]
|
||||
|
||||
if not issubclass(type(model), request.env.registry['microsoft.outlook.mixin']):
|
||||
if not isinstance(model, request.env.registry['microsoft.outlook.mixin']):
|
||||
# The model must inherits from the "microsoft.outlook.mixin" mixin
|
||||
raise Forbidden()
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ class MailController(mail.MailController):
|
||||
if not model or not res_id or model not in request.env:
|
||||
return super(MailController, cls)._redirect_to_record(model, res_id, access_token=access_token, **kwargs)
|
||||
|
||||
if issubclass(type(request.env[model]), request.env.registry['portal.mixin']):
|
||||
if isinstance(request.env[model], request.env.registry['portal.mixin']):
|
||||
uid = request.session.uid or request.env.ref('base.public_user').id
|
||||
record_sudo = request.env[model].sudo().browse(res_id).exists()
|
||||
try:
|
||||
|
||||
@@ -24,7 +24,7 @@ class MailThread(models.AbstractModel):
|
||||
if not self:
|
||||
return groups
|
||||
|
||||
portal_enabled = isinstance(self, type(self.env['portal.mixin']))
|
||||
portal_enabled = isinstance(self, self.env.registry['portal.mixin'])
|
||||
if not portal_enabled:
|
||||
return groups
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class MailThread(models.AbstractModel):
|
||||
raise ValueError(_('Invalid token or rating.'))
|
||||
|
||||
rating.write({'rating': rate, 'feedback': feedback, 'consumed': True})
|
||||
if issubclass(type(self), self.env.registry['mail.thread']):
|
||||
if isinstance(self, self.env.registry['mail.thread']):
|
||||
if subtype_xmlid is None:
|
||||
subtype_id = self._rating_apply_get_default_subtype_id()
|
||||
else:
|
||||
|
||||
@@ -103,7 +103,7 @@ class SendSMS(models.TransientModel):
|
||||
continue
|
||||
|
||||
records = composer._get_records()
|
||||
if records and issubclass(type(records), self.pool['mail.thread']):
|
||||
if records and isinstance(records, self.pool['mail.thread']):
|
||||
res = records._sms_get_recipients_info(force_field=composer.number_field_name, partner_fallback=not composer.comment_single_recipient)
|
||||
composer.recipient_valid_count = len([rid for rid, rvalues in res.items() if rvalues['sanitized']])
|
||||
composer.recipient_invalid_count = len([rid for rid, rvalues in res.items() if not rvalues['sanitized']])
|
||||
@@ -116,7 +116,7 @@ class SendSMS(models.TransientModel):
|
||||
def _compute_recipient_single_stored(self):
|
||||
for composer in self:
|
||||
records = composer._get_records()
|
||||
if not records or not issubclass(type(records), self.pool['mail.thread']) or not composer.comment_single_recipient:
|
||||
if not records or not isinstance(records, self.pool['mail.thread']) or not composer.comment_single_recipient:
|
||||
composer.recipient_single_number_itf = ''
|
||||
continue
|
||||
records.ensure_one()
|
||||
@@ -130,7 +130,7 @@ class SendSMS(models.TransientModel):
|
||||
def _compute_recipient_single_non_stored(self):
|
||||
for composer in self:
|
||||
records = composer._get_records()
|
||||
if not records or not issubclass(type(records), self.pool['mail.thread']) or not composer.comment_single_recipient:
|
||||
if not records or not isinstance(records, self.pool['mail.thread']) or not composer.comment_single_recipient:
|
||||
composer.recipient_single_description = False
|
||||
composer.recipient_single_number = ''
|
||||
continue
|
||||
@@ -194,7 +194,7 @@ class SendSMS(models.TransientModel):
|
||||
if self.composition_mode == 'numbers':
|
||||
return self._action_send_sms_numbers()
|
||||
elif self.composition_mode == 'comment':
|
||||
if records is None or not issubclass(type(records), self.pool['mail.thread']):
|
||||
if records is None or not isinstance(records, self.pool['mail.thread']):
|
||||
return self._action_send_sms_numbers()
|
||||
if self.comment_single_recipient:
|
||||
return self._action_send_sms_comment_single(records)
|
||||
@@ -240,7 +240,7 @@ class SendSMS(models.TransientModel):
|
||||
|
||||
sms_record_values = self._prepare_mass_sms_values(records)
|
||||
sms_all = self._prepare_mass_sms(records, sms_record_values)
|
||||
if sms_all and self.mass_keep_log and records and issubclass(type(records), self.pool['mail.thread']):
|
||||
if sms_all and self.mass_keep_log and records and isinstance(records, self.pool['mail.thread']):
|
||||
log_values = self._prepare_mass_log_values(records, sms_record_values)
|
||||
records._message_log_batch(**log_values)
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ class BaseDocumentLayout(models.TransientModel):
|
||||
if not logo:
|
||||
return False, False
|
||||
# The "===" gives different base64 encoding a correct padding
|
||||
logo += b'===' if type(logo) == bytes else '==='
|
||||
logo += b'===' if isinstance(logo, bytes) else '==='
|
||||
try:
|
||||
# Catches exceptions caused by logo not being an image
|
||||
image = tools.image_fix_orientation(tools.base64_to_image(logo))
|
||||
|
||||
@@ -39,7 +39,7 @@ class Image(models.AbstractModel):
|
||||
sha = hashlib.sha512(str(getattr(record, 'write_date', fields.Datetime.now())).encode('utf-8')).hexdigest()[:7]
|
||||
max_size = '' if max_size is None else '/%s' % max_size
|
||||
|
||||
if options.get('filename-field') and getattr(record, options['filename-field'], None):
|
||||
if options.get('filename-field') and options['filename-field'] in record and record[options['filename-field']]:
|
||||
filename = record[options['filename-field']]
|
||||
elif options.get('filename'):
|
||||
filename = options['filename']
|
||||
@@ -73,7 +73,7 @@ class Image(models.AbstractModel):
|
||||
|
||||
src, src_zoom = self._get_src_urls(record, field_name, options)
|
||||
|
||||
if options.get('alt-field') and getattr(record, options['alt-field'], None):
|
||||
if options.get('alt-field') and options['alt-field'] in record and record[options['alt-field']]:
|
||||
alt = escape(record[options['alt-field']])
|
||||
elif options.get('alt'):
|
||||
alt = options['alt']
|
||||
|
||||
@@ -211,7 +211,7 @@ class ManyToOne(models.AbstractModel):
|
||||
def attributes(self, record, field_name, options, values):
|
||||
attrs = super(ManyToOne, self).attributes(record, field_name, options, values)
|
||||
if options.get('inherit_branding'):
|
||||
many2one = getattr(record, field_name)
|
||||
many2one = record[field_name]
|
||||
if many2one:
|
||||
attrs['data-oe-many2one-id'] = many2one.id
|
||||
attrs['data-oe-many2one-model'] = many2one._name
|
||||
|
||||
@@ -188,7 +188,7 @@ class IrActions(models.Model):
|
||||
:return: A read() view of the ir.actions.action safe for web use
|
||||
"""
|
||||
record = self.env.ref(full_xml_id)
|
||||
assert isinstance(self.env[record._name], type(self))
|
||||
assert isinstance(self.env[record._name], self.env.registry[self._name])
|
||||
return record._get_action_dict()
|
||||
|
||||
def _get_action_dict(self):
|
||||
@@ -725,7 +725,7 @@ class IrActionsServer(models.Model):
|
||||
|
||||
def _get_runner(self):
|
||||
multi = True
|
||||
t = type(self)
|
||||
t = self.env.registry[self._name]
|
||||
fn = getattr(t, f'_run_action_{self.state}_multi', None)\
|
||||
or getattr(t, f'run_action_{self.state}_multi', None)
|
||||
if not fn:
|
||||
@@ -739,7 +739,7 @@ class IrActionsServer(models.Model):
|
||||
def _register_hook(self):
|
||||
super()._register_hook()
|
||||
|
||||
for cls in type(self).mro():
|
||||
for cls in self.env.registry[self._name].mro():
|
||||
for symbol in vars(cls).keys():
|
||||
if symbol.startswith('run_action_'):
|
||||
_logger.warning(
|
||||
|
||||
@@ -31,7 +31,7 @@ class AutoVacuum(models.AbstractModel):
|
||||
raise AccessDenied()
|
||||
|
||||
for model in self.env.values():
|
||||
cls = type(model)
|
||||
cls = self.env.registry[self._name]
|
||||
for attr, func in inspect.getmembers(cls, is_autovacuum):
|
||||
_logger.debug('Calling %s.%s()', model, attr)
|
||||
try:
|
||||
|
||||
@@ -384,7 +384,7 @@ class IrModel(models.Model):
|
||||
'model': model._name,
|
||||
'name': model._description,
|
||||
'order': model._order,
|
||||
'info': next(cls.__doc__ for cls in type(model).mro() if cls.__doc__),
|
||||
'info': next(cls.__doc__ for cls in self.env.registry[model._name].mro() if cls.__doc__),
|
||||
'state': 'manual' if model._custom else 'base',
|
||||
'transient': model._transient,
|
||||
}
|
||||
@@ -1859,7 +1859,7 @@ class IrModelConstraint(models.Model):
|
||||
# map each constraint on the name of the module where it is defined
|
||||
constraint_module = {
|
||||
constraint[0]: cls._module
|
||||
for cls in reversed(type(model).mro())
|
||||
for cls in reversed(self.env.registry[model._name].mro())
|
||||
if models.is_definition_class(cls)
|
||||
for constraint in getattr(cls, '_local_sql_constraints', ())
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@ class Module(models.Model):
|
||||
# configure the CoA on his own company, which makes no sense.
|
||||
if request:
|
||||
request.allowed_company_ids = self.env.companies.ids
|
||||
return self._button_immediate_function(type(self).button_install)
|
||||
return self._button_immediate_function(self.env.registry[self._name].button_install)
|
||||
|
||||
@assert_log_admin_access
|
||||
def button_install_cancel(self):
|
||||
@@ -615,7 +615,7 @@ class Module(models.Model):
|
||||
returns the next res.config action to execute
|
||||
"""
|
||||
_logger.info('User #%d triggered module uninstallation', self.env.uid)
|
||||
return self._button_immediate_function(type(self).button_uninstall)
|
||||
return self._button_immediate_function(self.env.registry[self._name].button_uninstall)
|
||||
|
||||
@assert_log_admin_access
|
||||
def button_uninstall(self):
|
||||
@@ -653,7 +653,7 @@ class Module(models.Model):
|
||||
Upgrade the selected module(s) immediately and fully,
|
||||
return the next res.config action to execute
|
||||
"""
|
||||
return self._button_immediate_function(type(self).button_upgrade)
|
||||
return self._button_immediate_function(self.env.registry[self._name].button_upgrade)
|
||||
|
||||
@assert_log_admin_access
|
||||
def button_upgrade(self):
|
||||
|
||||
@@ -809,7 +809,7 @@ class QwebView(models.AbstractModel):
|
||||
|
||||
@api.model
|
||||
def record_to_html(self, record, field_name, options):
|
||||
view = getattr(record, field_name)
|
||||
view = record[field_name]
|
||||
if not view:
|
||||
return ''
|
||||
|
||||
|
||||
@@ -628,8 +628,8 @@ actual arch.
|
||||
views = self.browse(row[0] for row in rows)
|
||||
|
||||
# optimization: fill in cache of inherit_id and mode
|
||||
self.env.cache.update(views, type(self).inherit_id, [row[1] for row in rows])
|
||||
self.env.cache.update(views, type(self).mode, [row[2] for row in rows])
|
||||
self.env.cache.update(views, self._fields['inherit_id'], [row[1] for row in rows])
|
||||
self.env.cache.update(views, self._fields['mode'], [row[2] for row in rows])
|
||||
|
||||
# During an upgrade, we can only use the views that have been
|
||||
# fully upgraded already.
|
||||
@@ -1266,7 +1266,7 @@ actual arch.
|
||||
if func is not None:
|
||||
return func(node, name_manager)
|
||||
# by default views are non-editable
|
||||
return node.tag not in (item[0] for item in type(self).type.selection)
|
||||
return node.tag not in (item[0] for item in self._fields['type'].selection)
|
||||
|
||||
def _editable_tag_form(self, node, name_manager):
|
||||
return True
|
||||
@@ -1488,7 +1488,7 @@ actual arch.
|
||||
elif not name:
|
||||
self._raise_view_error(_("Button must have a name"), node)
|
||||
elif type_ == 'object':
|
||||
func = getattr(type(name_manager.model), name, None)
|
||||
func = getattr(name_manager.model, name, None)
|
||||
if not func:
|
||||
msg = _(
|
||||
"%(action_name)s is not a valid action on %(model_name)s",
|
||||
@@ -1504,7 +1504,7 @@ actual arch.
|
||||
)
|
||||
self._raise_view_error(msg, node)
|
||||
try:
|
||||
inspect.signature(func).bind(self=name_manager.model)
|
||||
inspect.signature(func).bind()
|
||||
except TypeError:
|
||||
msg = "%s on %s has parameters and cannot be called from a button"
|
||||
self._log_view_warning(msg % (name, name_manager.model._name), node)
|
||||
|
||||
@@ -1000,7 +1000,7 @@ class Partner(models.Model):
|
||||
'company_name': self.commercial_company_name or '',
|
||||
})
|
||||
for field in self._formatting_address_fields():
|
||||
args[field] = getattr(self, field) or ''
|
||||
args[field] = self[field] or ''
|
||||
if without_company:
|
||||
args['company_name'] = ''
|
||||
elif self.commercial_company_name:
|
||||
|
||||
@@ -1340,7 +1340,7 @@ class UsersImplied(models.Model):
|
||||
user = self.new(values)
|
||||
gs = user.groups_id._origin
|
||||
gs = gs | gs.trans_implied_ids
|
||||
values['groups_id'] = type(self).groups_id.convert_to_write(gs, user)
|
||||
values['groups_id'] = self._fields['groups_id'].convert_to_write(gs, user)
|
||||
return super(UsersImplied, self).create(vals_list)
|
||||
|
||||
def write(self, values):
|
||||
|
||||
@@ -406,7 +406,7 @@ class MergePartnerAutomatic(models.TransientModel):
|
||||
|
||||
for field_name in self._fields:
|
||||
if field_name.startswith(group_by_prefix):
|
||||
if getattr(self, field_name, False):
|
||||
if field_name in self and self[field_name]:
|
||||
groups.append(field_name[len(group_by_prefix):])
|
||||
|
||||
if not groups:
|
||||
|
||||
@@ -25,7 +25,7 @@ class m(models.Model):
|
||||
@api.model
|
||||
def leak_memory(self, size):
|
||||
if not hasattr(self, 'l'):
|
||||
type(self).l = []
|
||||
self.env.registry[self._name].l = []
|
||||
self.l.append([0] * size)
|
||||
return True
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class GroupOnSelection(models.Model):
|
||||
|
||||
def _expand_states(self, states, domain, order):
|
||||
# return all possible states, in order
|
||||
return [key for key, val in type(self).state.selection]
|
||||
return [key for key, val in self._fields['state'].selection]
|
||||
|
||||
|
||||
class FillTemporal(models.Model):
|
||||
|
||||
+3
-1
@@ -456,7 +456,9 @@ def _call_kw_multi(method, self, args, kwargs):
|
||||
|
||||
def call_kw(model, name, args, kwargs):
|
||||
""" Invoke the given method ``name`` on the recordset ``model``. """
|
||||
method = getattr(type(model), name)
|
||||
method = getattr(type(model), name, None)
|
||||
if not method:
|
||||
raise AttributeError(f"The method '{name}' does not exist on the model '{model._name}'")
|
||||
api = getattr(method, '_api', None)
|
||||
if api == 'model':
|
||||
result = _call_kw_model(method, model, args, kwargs)
|
||||
|
||||
+13
-13
@@ -637,7 +637,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
@api.model
|
||||
def _add_field(self, name, field):
|
||||
""" Add the given ``field`` under the given ``name`` in the class """
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
|
||||
# Assert the name is an existing field in the model, or any model in the _inherits
|
||||
# or a custom field (starting by `x_`)
|
||||
@@ -667,7 +667,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
""" Remove the field with the given ``name`` from the model.
|
||||
This method should only be used for manual fields.
|
||||
"""
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
field = cls._fields.pop(name, None)
|
||||
discardattr(cls, name)
|
||||
if cls._rec_name == name:
|
||||
@@ -852,7 +852,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
return func(self)
|
||||
return wrapper
|
||||
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
methods = []
|
||||
for attr, func in getmembers(cls, is_constraint):
|
||||
if callable(func._constrains):
|
||||
@@ -875,7 +875,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
def is_ondelete(func):
|
||||
return callable(func) and hasattr(func, '_ondelete')
|
||||
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
methods = [func for _, func in getmembers(cls, is_ondelete)]
|
||||
# optimization: memoize results on cls, it will not be recomputed
|
||||
cls._ondelete_methods = methods
|
||||
@@ -888,7 +888,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
return callable(func) and hasattr(func, '_onchange')
|
||||
|
||||
# collect onchange methods on the model's class
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
methods = defaultdict(list)
|
||||
for attr, func in getmembers(cls, is_onchange):
|
||||
missing = []
|
||||
@@ -1102,7 +1102,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
# collect all the tuples in "lines" (along with their coordinates)
|
||||
for i, line in enumerate(lines):
|
||||
for j, cell in enumerate(line):
|
||||
if type(cell) is tuple:
|
||||
if isinstance(cell, tuple):
|
||||
bymodels[cell[0]].add(cell[1])
|
||||
xidmap[cell].append((i, j))
|
||||
# for each model, xid-export everything and inject in matrix
|
||||
@@ -2196,7 +2196,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
# columns should be displayed even if they don't contain any record.
|
||||
group_expand = field.group_expand
|
||||
if isinstance(group_expand, str):
|
||||
group_expand = getattr(type(self), group_expand)
|
||||
group_expand = getattr(self.env.registry[self._name], group_expand)
|
||||
assert callable(group_expand)
|
||||
|
||||
# determine all groups that should be returned
|
||||
@@ -3253,13 +3253,13 @@ class BaseModel(metaclass=MetaModel):
|
||||
field.required = True
|
||||
if field.ondelete.lower() not in ('cascade', 'restrict'):
|
||||
field.ondelete = 'cascade'
|
||||
type(self)._inherits = {**self._inherits, field.comodel_name: field.name}
|
||||
self.pool[self._name]._inherits = {**self._inherits, field.comodel_name: field.name}
|
||||
self.pool[field.comodel_name]._inherits_children.add(self._name)
|
||||
|
||||
@api.model
|
||||
def _prepare_setup(self):
|
||||
""" Prepare the setup of the model. """
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
cls._setup_done = False
|
||||
|
||||
# changing base classes is costly, do it only when necessary
|
||||
@@ -3273,7 +3273,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
@api.model
|
||||
def _setup_base(self):
|
||||
""" Determine the inherited and custom fields of the model. """
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
if cls._setup_done:
|
||||
return
|
||||
|
||||
@@ -3358,7 +3358,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
@api.model
|
||||
def _setup_fields(self):
|
||||
""" Setup the fields, except for recomputation triggers. """
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
|
||||
# set up fields
|
||||
bad_fields = []
|
||||
@@ -3381,7 +3381,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
@api.model
|
||||
def _setup_complete(self):
|
||||
""" Setup recomputation triggers, and complete the model setup. """
|
||||
cls = type(self)
|
||||
cls = self.env.registry[self._name]
|
||||
|
||||
# register constraints and onchange methods
|
||||
cls._init_constraints_onchanges()
|
||||
@@ -6574,7 +6574,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
"""
|
||||
if isinstance(key, str):
|
||||
# important: one must call the field's getter
|
||||
return self._fields[key].__get__(self, type(self))
|
||||
return self._fields[key].__get__(self, self.env.registry[self._name])
|
||||
elif isinstance(key, slice):
|
||||
return self.browse(self._ids[key])
|
||||
else:
|
||||
|
||||
@@ -707,8 +707,8 @@ class Registry(Mapping):
|
||||
env = odoo.api.Environment(cr, SUPERUSER_ID, {})
|
||||
table2model = {
|
||||
model._table: name
|
||||
for name, model in env.items()
|
||||
if not model._abstract and model.__class__._table_query is None
|
||||
for name, model in env.registry.items()
|
||||
if not model._abstract and model._table_query is None
|
||||
}
|
||||
missing_tables = set(table2model).difference(existing_tables(cr, table2model))
|
||||
|
||||
|
||||
@@ -362,7 +362,7 @@ class CommonServer(object):
|
||||
cls._on_stop_funcs.append(func)
|
||||
|
||||
def stop(self):
|
||||
for func in type(self)._on_stop_funcs:
|
||||
for func in self._on_stop_funcs:
|
||||
try:
|
||||
_logger.debug("on_close call %s", func)
|
||||
func()
|
||||
|
||||
@@ -8,14 +8,24 @@ from dateutil.relativedelta import relativedelta
|
||||
from .func import lazy
|
||||
from odoo.loglevels import ustr
|
||||
|
||||
|
||||
def date_type(value):
|
||||
''' Return either the datetime.datetime class or datetime.date type whether `value` is a datetime or a date.
|
||||
|
||||
:param value: A datetime.datetime or datetime.date object.
|
||||
:return: datetime.datetime or datetime.date
|
||||
'''
|
||||
return datetime if isinstance(value, datetime) else date
|
||||
|
||||
|
||||
def get_month(date):
|
||||
''' Compute the month dates range on which the 'date' parameter belongs to.
|
||||
|
||||
:param date: A datetime.datetime or datetime.date object.
|
||||
:return: A tuple (date_from, date_to) having the same object type as the 'date' parameter.
|
||||
'''
|
||||
date_from = type(date)(date.year, date.month, 1)
|
||||
date_to = type(date)(date.year, date.month, calendar.monthrange(date.year, date.month)[1])
|
||||
date_from = date_type(date)(date.year, date.month, 1)
|
||||
date_to = date_type(date)(date.year, date.month, calendar.monthrange(date.year, date.month)[1])
|
||||
return date_from, date_to
|
||||
|
||||
|
||||
@@ -36,7 +46,7 @@ def get_quarter(date):
|
||||
'''
|
||||
quarter_number = get_quarter_number(date)
|
||||
month_from = ((quarter_number - 1) * 3) + 1
|
||||
date_from = type(date)(date.year, month_from, 1)
|
||||
date_from = date_type(date)(date.year, month_from, 1)
|
||||
date_to = (date_from + relativedelta(months=2))
|
||||
date_to = date_to.replace(day=calendar.monthrange(date_to.year, date_to.month)[1])
|
||||
return date_from, date_to
|
||||
@@ -60,18 +70,18 @@ def get_fiscal_year(date, day=31, month=12):
|
||||
return min(day, max_day)
|
||||
|
||||
day = fix_day(date.year, month, day)
|
||||
date_to = type(date)(date.year, month, day)
|
||||
date_to = date_type(date)(date.year, month, day)
|
||||
|
||||
if date <= date_to:
|
||||
date_from = date_to - relativedelta(years=1)
|
||||
day = fix_day(date_from.year, date_from.month, date_from.day)
|
||||
date_from = type(date)(date_from.year, date_from.month, day)
|
||||
date_from = date_type(date)(date_from.year, date_from.month, day)
|
||||
date_from += relativedelta(days=1)
|
||||
else:
|
||||
date_from = date_to + relativedelta(days=1)
|
||||
date_to = date_to + relativedelta(years=1)
|
||||
day = fix_day(date_to.year, date_to.month, date_to.day)
|
||||
date_to = type(date)(date_to.year, date_to.month, day)
|
||||
date_to = date_type(date)(date_to.year, date_to.month, day)
|
||||
return date_from, date_to
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -493,7 +493,8 @@ def append_content_to_html(html, content, plaintext=True, preserve=False, contai
|
||||
|
||||
def prepend_html_content(html_body, html_content):
|
||||
"""Prepend some HTML content at the beginning of an other HTML content."""
|
||||
html_content = type(html_content)(re.sub(r'(?i)(</?(?:html|body|head|!\s*DOCTYPE)[^>]*>)', '', html_content))
|
||||
replacement = re.sub(r'(?i)(</?(?:html|body|head|!\s*DOCTYPE)[^>]*>)', '', html_content)
|
||||
html_content = markupsafe.Markup(replacement) if isinstance(html_content, markupsafe.Markup) else replacement
|
||||
html_content = html_content.strip()
|
||||
|
||||
body_match = re.search(r'<body[^>]*>', html_body) or re.search(r'<html[^>]*>', html_body)
|
||||
|
||||
+2
-2
@@ -851,7 +851,7 @@ class lower_logging(logging.Handler):
|
||||
record.levelname = f'_{record.levelname}'
|
||||
record.levelno = self.to_level
|
||||
self.had_error_log = True
|
||||
record.args = tuple(arg.replace('Traceback (most recent call last):', '_Traceback_ (most recent call last):') if type(arg) is str else arg for arg in record.args) # pylint: disable=unidiomatic-typecheck
|
||||
record.args = tuple(arg.replace('Traceback (most recent call last):', '_Traceback_ (most recent call last):') if isinstance(arg, str) else arg for arg in record.args)
|
||||
|
||||
if logging.getLogger(record.name).isEnabledFor(record.levelno):
|
||||
for handler in self.old_handlers:
|
||||
@@ -1698,7 +1698,7 @@ class DotDict(dict):
|
||||
"""
|
||||
def __getattr__(self, attrib):
|
||||
val = self.get(attrib)
|
||||
return DotDict(val) if type(val) is dict else val
|
||||
return DotDict(val) if isinstance(val, dict) else val
|
||||
|
||||
|
||||
def get_diff(data_from, data_to, custom_style=False, dark_color_scheme=False):
|
||||
|
||||
Reference in New Issue
Block a user