Skip to content

Commit

Permalink
[FIX] P3: metaclasses handling
Browse files Browse the repository at this point in the history
* cross-version metaclass spec
* more formally deprecate browse_record and browse_null since they
  were using metaclasses anyway
* update docstrings referencing the latter
  • Loading branch information
xmo-odoo committed May 11, 2017
1 parent 048f122 commit 6659d5a
Show file tree
Hide file tree
Showing 16 changed files with 48 additions and 46 deletions.
2 changes: 1 addition & 1 deletion addons/account/models/account_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ def get_move_lines_for_manual_reconciliation(self, account_id, partner_id=False,
def prepare_move_lines_for_reconciliation_widget(self, target_currency=False, target_date=False):
""" Returns move lines formatted for the manual/bank reconciliation widget
:param target_currency: currency (browse_record or ID) you want the move line debit/credit converted into
:param target_currency: currency (Model or ID) you want the move line debit/credit converted into
:param target_date: date to use for the monetary conversion
"""
context = dict(self._context or {})
Expand Down
2 changes: 1 addition & 1 deletion addons/account/models/chart_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ def _get_chart_parent_ids(self, chart_template):
""" Returns the IDs of all ancestor charts, including the chart itself.
(inverse of child_of operator)
:param browse_record chart_template: the account.chart.template record
:param BaseModel chart_template: the account.chart.template record
:return: the IDS of all ancestor charts, including the chart itself.
"""
result = [chart_template.id]
Expand Down
2 changes: 1 addition & 1 deletion addons/base_import/models/base_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ def parse_preview(self, options, count=10):

@api.model
def _convert_import_data(self, fields, options):
""" Extracts the input browse_record and fields list (with
""" Extracts the input BaseModel and fields list (with
``False``-y placeholders for fields to *not* import) into a
format Model.import_data can use: a fields list without holes
and the precisely matching data matrix
Expand Down
4 changes: 1 addition & 3 deletions addons/mail/models/mail_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ def _postprocess_sent_message(self, mail_sent=True):
attachment if the ``auto_delete`` flag of the mail was set.
Overridden by subclasses for extra post-processing behaviors.
:param browse_record mail: the mail that was just sent
:return: True
"""
notif_emails = self.filtered(lambda email: email.notification)
Expand Down Expand Up @@ -180,8 +179,7 @@ def send_get_email_dict(self, partner=None):
"""Return a dictionary for specific email values, depending on a
partner, or generic to the whole recipients given by mail.email_to.
:param browse_record mail: mail.mail browse_record
:param browse_record partner: specific recipient partner
:param Model partner: specific recipient partner
"""
self.ensure_one()
body = self.send_get_mail_body(partner=partner)
Expand Down
2 changes: 1 addition & 1 deletion addons/mail/models/mail_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ def render_template(self, template_txt, model, res_ids, post_process=False):
with the result of evaluating these expressions with an evaluation
context containing:
- ``user``: browse_record of the current user
- ``user``: Model of the current user
- ``object``: record of the document record this mail is related to
- ``context``: the context passed to the mail composition wizard
Expand Down
3 changes: 1 addition & 2 deletions addons/marketing_campaign/models/marketing_campaign.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ def _find_duplicate_workitems(self, record):
"""Finds possible duplicates workitems for a record in this campaign, based on a uniqueness
field.
:param record: browse_record to find duplicates workitems for.
:param campaign_rec: browse_record of campaign
:param Model record: to find duplicates workitems for.
"""
self.ensure_one()
duplicate_workitem_domain = [('res_id', '=', record.id), ('campaign_id', '=', self.id)]
Expand Down
8 changes: 4 additions & 4 deletions addons/purchase/models/purchase.py
Original file line number Diff line number Diff line change
Expand Up @@ -711,10 +711,10 @@ def _get_date_planned(self, seller, po=False):
PO Lines that correspond to the given product.seller_ids,
when ordered at `date_order_str`.
:param browse_record | False product: product.product, used to
determine delivery delay thanks to the selected seller field (if False, default delay = 0)
:param browse_record | False po: purchase.order, necessary only if
the PO line is not yet attached to a PO.
:param Model seller: used to fetch the delivery delay (if no seller
is provided, the delay is 0)
:param Model po: purchase.order, necessary only if the PO line is
not yet attached to a PO.
:rtype: datetime
:return: desired Schedule Date for the PO line
"""
Expand Down
6 changes: 2 additions & 4 deletions addons/website/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@
import werkzeug.wrappers

import odoo
from odoo import http
from odoo import http, models
from odoo import fields
from odoo.http import request
from odoo.osv.orm import browse_record
from odoo.exceptions import AccessError

from odoo.addons.website.models.website import slug
from odoo.addons.web.controllers.main import WebClient, Binary, Home
Expand Down Expand Up @@ -45,7 +43,7 @@ def __call__(self, path=None, path_args=None, **kw):
paths, fragments = [], []
for key, value in pycompat.items(kw):
if value and key in path_args:
if isinstance(value, browse_record):
if isinstance(value, models.BaseModel):
paths.append((key, slug(value)))
else:
paths.append((key, value))
Expand Down
4 changes: 2 additions & 2 deletions odoo/addons/base/ir/ir_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1304,8 +1304,8 @@ def xmlid_to_res_id(self, xmlid, raise_if_not_found=False):

@api.model
def xmlid_to_object(self, xmlid, raise_if_not_found=False):
""" Return a browse_record
if not found and raise_if_not_found is False return None
""" Return a Model object, or ``None`` if ``raise_if_not_found`` is
set
"""
t = self.xmlid_to_res_model_res_id(xmlid, raise_if_not_found)
res_model, res_id = t
Expand Down
5 changes: 3 additions & 2 deletions odoo/addons/base/ir/ir_qweb/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ def value_to_html(self, value, options):
def record_to_html(self, record, field_name, options):
""" record_to_html(record, field_name, options)
Converts the specified field of the browse_record ``record`` to HTML
Converts the specified field of the ``record`` to HTML
:rtype: unicode
"""
if not record:
Expand All @@ -110,7 +111,7 @@ def user_lang(self):
in the user's context. Fallbacks to en_US if no lang is present in the
context *or the language code is not valid*.
:returns: res.lang browse_record
:returns: Model[res.lang]
"""
lang_code = self._context.get('lang') or 'en_US'
return self.env['res.lang']._lang_get(lang_code)
Expand Down
2 changes: 2 additions & 0 deletions odoo/addons/test_pylint/tests/test_pylint.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class TestPyLint(TransactionCase):
'dict-view-method',

'long-suffix',

'metaclass-assignment',
]

BAD_FUNCTIONS = [
Expand Down
7 changes: 1 addition & 6 deletions odoo/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@ def __init__(cls, name, bases, attrs):
if name != 'command':
commands[name] = cls

class Command(object):
"""Subclass this class to define new odoo subcommands """
__metaclass__ = CommandType

def run(self, args):
pass
Command = CommandType('Command', (object,), {'run': lambda self, args: None})

class Help(Command):
"""Display the list of available commands"""
Expand Down
6 changes: 4 additions & 2 deletions odoo/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ def __new__(meta, name, bases, attrs):

def __init__(cls, name, bases, attrs):
super(MetaField, cls).__init__(name, bases, attrs)
if not hasattr(cls, 'type'):
return

if cls.type and cls.type not in MetaField.by_type:
MetaField.by_type[cls.type] = cls

Expand All @@ -118,7 +121,7 @@ def __init__(cls, name, bases, attrs):
cls.description_attrs.append((attr[13:], attr))


class Field(object):
class Field(MetaField('DummyField', (object,), {})):
""" The field descriptor contains the field definition, and manages accesses
and assignments of the corresponding field on records. The following
attributes may be provided when instanciating a field:
Expand Down Expand Up @@ -280,7 +283,6 @@ class Second(models.Model):
state = fields.Selection(help="Blah blah blah")
"""
__metaclass__ = MetaField

type = None # type of the field (string)
relational = False # whether the field is a relational one
Expand Down
3 changes: 1 addition & 2 deletions odoo/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,8 +908,7 @@ def __init__(cls, name, bases, attrs):
return
controllers_per_module[module].append(name_class)

class Controller(object):
__metaclass__ = ControllerType
Controller = ControllerType('Controller', (object,), {})

class EndPoint(object):
def __init__(self, method, routing):
Expand Down
6 changes: 2 additions & 4 deletions odoo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,13 @@
from lxml.builder import E

import odoo
from odoo.tools import pycompat
from . import SUPERUSER_ID
from . import api
from . import tools
from .exceptions import AccessError, MissingError, ValidationError, UserError
from .osv.query import Query
from .tools import frozendict, lazy_classproperty, lazy_property, ormcache, \
Collector, LastOrderedSet, OrderedSet
Collector, LastOrderedSet, OrderedSet, pycompat
from .tools.config import config
from .tools.func import frame_codeinfo
from .tools.misc import CountingStream, DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
Expand Down Expand Up @@ -182,7 +181,7 @@ def __nonzero__(self):
MAGIC_COLUMNS = ['id'] + LOG_ACCESS_COLUMNS


class BaseModel(object):
class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
""" Base class for Odoo models.
Odoo models are created by inheriting:
Expand Down Expand Up @@ -210,7 +209,6 @@ class BaseModel(object):
To create a class that should not be instantiated, the _register class
attribute may be set to False.
"""
__metaclass__ = MetaModel
_auto = False # don't create any database backend
_register = False # not visible in ORM registry
_abstract = True # whether model is abstract
Expand Down
32 changes: 21 additions & 11 deletions odoo/osv/orm.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import json
import warnings

from lxml import etree

from odoo.tools import pycompat
Expand All @@ -17,17 +19,25 @@
# extra definitions for backward compatibility
browse_record_list = BaseModel

class browse_record(object):
""" Pseudo-class for testing record instances """
class __metaclass__(type):
def __instancecheck__(self, inst):
return isinstance(inst, BaseModel) and len(inst) <= 1

class browse_null(object):
""" Pseudo-class for testing null instances """
class __metaclass__(type):
def __instancecheck__(self, inst):
return isinstance(inst, BaseModel) and not inst
class BRM(type):
def __instancecheck__(self, inst):
warnings.warn(DeprecationWarning(
"browse_record is a deprecated concept and should not be used "
"anymore, you can replace `isinstance(o, browse_record)` by "
"`isinstance(o, BaseModel)`"
))
return isinstance(inst, BaseModel) and len(inst) <= 1
browse_record = BRM('browse_record', (object,), {})

class NBM(type):
def __instancecheck__(self, inst):
warnings.warn(DeprecationWarning(
"browse_record is a deprecated concept and should not be used "
"anymore, you can replace `isinstance(o, browse_null)` by "
"`isinstance(o, BaseModel) and not o`"
))
return isinstance(inst, BaseModel) and not inst
browse_null = NBM('browse_null', (object,), {})


def transfer_field_to_modifiers(field, modifiers):
Expand Down

0 comments on commit 6659d5a

Please sign in to comment.