[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:
bve-odoo
2023-11-07 17:09:34 +00:00
parent 062fbb2c8d
commit 72c1a4f96a
45 changed files with 106 additions and 94 deletions
@@ -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', {}))
+2 -2
View File
@@ -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
# --------------------------------------------------------------------------------
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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]
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -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:
+3 -3
View File
@@ -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
+3 -4
View File
@@ -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()
+1 -1
View File
@@ -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 '')
+2 -2
View File
@@ -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']
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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 = [
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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:
+5 -5
View File
@@ -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)
+1 -1
View File
@@ -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))
+2 -2
View File
@@ -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']
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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(
+1 -1
View File
@@ -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:
+2 -2
View File
@@ -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', ())
}
+3 -3
View File
@@ -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):
+1 -1
View File
@@ -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 ''
+5 -5
View File
@@ -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)
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+2 -2
View File
@@ -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))
+1 -1
View File
@@ -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()
+16 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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):