diff --git a/modules/account_dunning_email/account.py b/modules/account_dunning_email/account.py index 5421d65d44..299a28afda 100644 --- a/modules/account_dunning_email/account.py +++ b/modules/account_dunning_email/account.py @@ -1,7 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.config import config +import trytond.config as config from trytond.model import fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval diff --git a/modules/account_dunning_email/ir.py b/modules/account_dunning_email/ir.py index 21f2c4cf77..e45c44e930 100644 --- a/modules/account_dunning_email/ir.py +++ b/modules/account_dunning_email/ir.py @@ -4,8 +4,7 @@ from sql import Table from sql.operators import Concat -from trytond import backend -from trytond.config import config +from trytond import backend, config from trytond.model import fields from trytond.pool import PoolMeta from trytond.pyson import Eval diff --git a/modules/account_es_sii/account.py b/modules/account_es_sii/account.py index f072a388f6..4369058a6a 100644 --- a/modules/account_es_sii/account.py +++ b/modules/account_es_sii/account.py @@ -9,7 +9,7 @@ from zeep import Client from zeep.exceptions import Error as ZeepError from zeep.transports import Transport -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import Index, ModelSQL, ModelView, Unique, Workflow, fields from trytond.model.exceptions import AccessError @@ -21,9 +21,6 @@ from trytond.transaction import Transaction from .exceptions import ESSIIPostedInvoicesError -CERT = ( - config.get('account_es_sii', 'certificate'), - config.get('account_es_sii', 'privatekey')) SEND_SIZE = 10000 @@ -79,15 +76,19 @@ class CredentialSII(ModelSQL, CompanyValueMixin): def get_client(cls, endpoint, **pattern): pool = Pool() Configuration = pool.get('account.configuration') - config = Configuration(1) - url = WS_URL.get(config.get_multivalue('es_sii_url', **pattern), '') + configuration = Configuration(1) + url = WS_URL.get( + configuration.get_multivalue('es_sii_url', **pattern), '') if not url: raise AccessError( gettext('account_es_sii.msg_missing_sii_url')) service = endpoint - environment = config.get_multivalue('es_sii_environment', **pattern) + environment = configuration.get_multivalue( + 'es_sii_environment', **pattern) session = Session() - session.cert = CERT + session.cert = ( + config.get('account_es_sii', 'certificate'), + config.get('account_es_sii', 'privatekey')) transport = Transport(session=session) client = Client(url + endpoint + '.wsdl', transport=transport) if environment == 'staging': diff --git a/modules/account_export/account.py b/modules/account_export/account.py index 21d9ef6e6a..de8242ff66 100644 --- a/modules/account_export/account.py +++ b/modules/account_export/account.py @@ -1,7 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ModelSQL, ModelView, Workflow, fields from trytond.model.exceptions import AccessError diff --git a/modules/account_fr/account.py b/modules/account_fr/account.py index 5e00d85e23..966d8a4754 100644 --- a/modules/account_fr/account.py +++ b/modules/account_fr/account.py @@ -7,18 +7,13 @@ from sql import Table from sql.aggregate import Sum from sql.conditionals import Coalesce -from trytond.config import config +import trytond.config as config from trytond.model import ModelStorage, ModelView, fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval from trytond.transaction import Transaction from trytond.wizard import Button, StateTransition, StateView, Wizard -OPENING_CODE = config.get('account_fr', 'fec_opening_code', default="OUV") -OPENING_NAME = config.get( - 'account_fr', 'fec_opening_name', default="Balance Initiale") -OPENING_NUMBER = config.get('account_fr', 'fec_opening_number', default="0") - class AccountTemplate(metaclass=PoolMeta): __name__ = 'account.account.template' @@ -215,9 +210,15 @@ class FrFEC(Wizard): else: debit, credit = 0, -balance yield [ - OPENING_CODE, - OPENING_NAME, - OPENING_NUMBER, + config.get( + 'account_fr', 'fec_opening_code', + default="OUV"), + config.get( + 'account_fr', 'fec_opening_name', + default="Balance Initiale"), + config.get( + 'account_fr', 'fec_opening_number', + default="0"), format_date(self.start.fiscalyear.start_date), code, name, diff --git a/modules/account_fr_chorus/account.py b/modules/account_fr_chorus/account.py index b18374720d..b0f0df037f 100644 --- a/modules/account_fr_chorus/account.py +++ b/modules/account_fr_chorus/account.py @@ -10,7 +10,7 @@ from oauthlib.oauth2 import BackendApplicationClient, TokenExpiredError from requests_oauthlib import OAuth2Session from sql.functions import CharLength -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ModelSQL, ModelView, Unique, Workflow, fields from trytond.model.exceptions import AccessError @@ -35,7 +35,6 @@ EDOC2SYNTAX = { EDOC2FILENAME = { 'edocument.uncefact.invoice': 'UNCEFACT-%s.xml', } -TIMEOUT = config.getfloat('account_fr_chorus', 'requests_timeout', default=300) if config.getboolean('account_fr_chorus', 'filestore', default=False): file_id = 'data_file_id' store_prefix = config.get( @@ -150,15 +149,17 @@ class CredentialChorus(ModelSQL, CompanyValueMixin): headers = { 'cpro-account': base64.b64encode(account.encode('utf-8')), } + timeout = config.getfloat( + 'account_fr_chorus', 'requests_timeout', default=300) try: resp = session.post( url, headers=headers, json=payload, - verify=True, timeout=TIMEOUT) + verify=True, timeout=timeout) except TokenExpiredError: cls._get_token(session) resp = session.post( url, headers=headers, json=payload, - verify=True, timeout=TIMEOUT) + verify=True, timeout=timeout) resp.raise_for_status() return resp.json() diff --git a/modules/account_invoice/invoice.py b/modules/account_invoice/invoice.py index dce39cdf67..4d6485c036 100644 --- a/modules/account_invoice/invoice.py +++ b/modules/account_invoice/invoice.py @@ -12,8 +12,7 @@ from sql.conditionals import Coalesce from sql.functions import CharLength, Round from sql.operators import Exists -from trytond import backend -from trytond.config import config +from trytond import backend, config from trytond.i18n import gettext from trytond.model import ( DeactivableMixin, Index, ModelSQL, ModelView, Unique, Workflow, dualmethod, diff --git a/modules/account_payment_braintree/account.py b/modules/account_payment_braintree/account.py index 490796b023..9d6b69bf97 100644 --- a/modules/account_payment_braintree/account.py +++ b/modules/account_payment_braintree/account.py @@ -14,8 +14,8 @@ except ImportError: class GatewayTimeoutError(Exception): pass +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.i18n import gettext from trytond.model import ( DeactivableMixin, Index, ModelSQL, ModelView, Workflow, fields) diff --git a/modules/account_payment_sepa/payment.py b/modules/account_payment_sepa/payment.py index 05a1cd368e..5e9902ef57 100644 --- a/modules/account_payment_sepa/payment.py +++ b/modules/account_payment_sepa/payment.py @@ -15,7 +15,7 @@ from genshi.template.astutil import ASTCodeGenerator, ASTTransformer from lxml import etree from sql import Literal, Null -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ( Index, ModelSQL, ModelView, Unique, Workflow, dualmethod, fields) diff --git a/modules/account_payment_stripe/payment.py b/modules/account_payment_stripe/payment.py index 63a2255b66..30f0e39d41 100644 --- a/modules/account_payment_stripe/payment.py +++ b/modules/account_payment_stripe/payment.py @@ -10,8 +10,8 @@ from operator import attrgetter import stripe +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.i18n import gettext from trytond.model import ( DeactivableMixin, Index, ModelSQL, ModelView, Unique, Workflow, dualmethod, diff --git a/modules/account_statement/statement.py b/modules/account_statement/statement.py index f67a8b6160..07c7bb708f 100644 --- a/modules/account_statement/statement.py +++ b/modules/account_statement/statement.py @@ -10,7 +10,7 @@ from sql.conditionals import Coalesce from sql.functions import CharLength from sql.operators import Concat -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ( Check, DictSchemaMixin, Index, ModelSQL, ModelView, Workflow, fields, diff --git a/modules/authentication_saml/routes.py b/modules/authentication_saml/routes.py index bb84014dd7..7ba7e05f78 100644 --- a/modules/authentication_saml/routes.py +++ b/modules/authentication_saml/routes.py @@ -13,7 +13,7 @@ import saml2 import saml2.client import saml2.config -from trytond.config import config +import trytond.config as config from trytond.protocols.dispatcher import register_authentication_service from trytond.protocols.wrappers import ( Response, abort, allow_null_origin, exceptions, redirect, with_pool, diff --git a/modules/authentication_sms/res.py b/modules/authentication_sms/res.py index fbe354409f..c0e5882f01 100644 --- a/modules/authentication_sms/res.py +++ b/modules/authentication_sms/res.py @@ -4,7 +4,7 @@ import datetime import logging import random -from trytond.config import config +import trytond.config as config from trytond.exceptions import LoginException from trytond.i18n import gettext from trytond.model import Index, ModelSQL, fields @@ -15,11 +15,10 @@ logger = logging.getLogger(__name__) def send_sms(text, to): - if config.has_option('authentication_sms', 'function'): - func = resolve(config.get('authentication_sms', 'function')) - if func: - from_ = config.get('authentication_sms', 'from', default=None) - return func(text, to, from_) + if func := config.get('authentication_sms', 'function', default=None): + func = resolve(func) + from_ = config.get('authentication_sms', 'from', default=None) + return func(text, to, from_) logger.error('Could not send SMS to %s: "%s"', to, text) diff --git a/modules/authentication_sms/tests/test_module.py b/modules/authentication_sms/tests/test_module.py index 779e526822..1d22fc68b6 100644 --- a/modules/authentication_sms/tests/test_module.py +++ b/modules/authentication_sms/tests/test_module.py @@ -2,7 +2,7 @@ # this repository contains the full copyright notices and license terms. import datetime -from trytond.config import config +import trytond.config as config from trytond.exceptions import LoginException from trytond.pool import Pool from trytond.tests.test_tryton import ModuleTestCase, with_transaction diff --git a/modules/company/company.py b/modules/company/company.py index 813d915fc7..e86f5e311d 100644 --- a/modules/company/company.py +++ b/modules/company/company.py @@ -11,7 +11,7 @@ try: except ImportError: PIL = None -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ( MatchMixin, ModelSQL, ModelView, Unique, fields, sequence_ordered) diff --git a/modules/currency/ir.py b/modules/currency/ir.py index 7f8e727a5a..ead7a504b8 100644 --- a/modules/currency/ir.py +++ b/modules/currency/ir.py @@ -1,6 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.config import config + +import trytond.config as config from trytond.model import fields from trytond.pool import PoolMeta diff --git a/modules/currency_ro/currency.py b/modules/currency_ro/currency.py index 4dae5a21d8..22458ad7bd 100644 --- a/modules/currency_ro/currency.py +++ b/modules/currency_ro/currency.py @@ -6,14 +6,13 @@ from decimal import Decimal import requests from lxml import etree -from trytond.config import config +import trytond.config as config from trytond.modules.currency.currency import CronFetchError from trytond.pool import PoolMeta from trytond.pyson import Eval, If URL_10DAYS = 'https://bnr.ro/nbrfxrates10days.xml' URL_YEAR = 'https://bnr.ro/files/xml/years/nbrfxrates%s.xml' -TIMEOUT = config.getfloat('currency_ro', 'requests_timeout', default=300) class Cron(metaclass=PoolMeta): @@ -35,8 +34,10 @@ class Cron(metaclass=PoolMeta): url = URL_10DAYS else: url = URL_YEAR % date.year + timeout = config.getfloat( + 'currency_ro', 'requests_timeout', default=300) try: - response = requests.get(url, timeout=TIMEOUT) + response = requests.get(url, timeout=timeout) except requests.HTTPError as e: raise CronFetchError() from e tree = etree.fromstring(response.content) diff --git a/modules/currency_rs/currency.py b/modules/currency_rs/currency.py index 53c4f80a8b..f9cda125ef 100644 --- a/modules/currency_rs/currency.py +++ b/modules/currency_rs/currency.py @@ -6,8 +6,8 @@ from lxml import objectify from zeep import Client, Transport from zeep.exceptions import Error +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.i18n import gettext from trytond.model import fields from trytond.modules.currency.currency import CronFetchError @@ -19,11 +19,11 @@ from .exceptions import RSCredentialWarning URL = ( 'https://webservices.nbs.rs/CommunicationOfficeService1_0/' 'ExchangeRateXmlService.asmx?WSDL') -TIMEOUT = config.getfloat('currency_rs', 'requests_timeout', default=300) def get_client(username, password, license_id): - client = Client(URL, transport=Transport(operation_timeout=TIMEOUT)) + timeout = config.getfloat('currency_rs', 'requests_timeout', default=300) + client = Client(URL, transport=Transport(operation_timeout=timeout)) client.set_default_soapheaders({ 'AuthenticationHeader': { 'UserName': username, diff --git a/modules/document_incoming/document.py b/modules/document_incoming/document.py index 3166a43b77..9085c16802 100644 --- a/modules/document_incoming/document.py +++ b/modules/document_incoming/document.py @@ -7,7 +7,7 @@ from io import BytesIO from pypdf import PdfReader, PdfWriter -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ( DeactivableMixin, ModelSingleton, ModelSQL, ModelView, Workflow, fields) diff --git a/modules/document_incoming/routes.py b/modules/document_incoming/routes.py index af2c789abe..80732277a2 100644 --- a/modules/document_incoming/routes.py +++ b/modules/document_incoming/routes.py @@ -3,7 +3,7 @@ import base64 -from trytond.config import config +import trytond.config as config from trytond.protocols.wrappers import ( HTTPStatus, Response, abort, set_max_request_size, user_application, with_pool, with_transaction) diff --git a/modules/inbound_email/inbound_email.py b/modules/inbound_email/inbound_email.py index 75bc1115d5..70ce79fc2a 100644 --- a/modules/inbound_email/inbound_email.py +++ b/modules/inbound_email/inbound_email.py @@ -11,7 +11,7 @@ from email.policy import default as email_policy_default from email.utils import getaddresses from functools import partial -from trytond.config import config +import trytond.config as config from trytond.model import ( ModelSQL, ModelStorage, ModelView, fields, sequence_ordered) from trytond.pool import Pool diff --git a/modules/inbound_email/routes.py b/modules/inbound_email/routes.py index 2deb781b36..2a6e0d0865 100644 --- a/modules/inbound_email/routes.py +++ b/modules/inbound_email/routes.py @@ -3,7 +3,7 @@ import json -from trytond.config import config +import trytond.config as config from trytond.protocols.wrappers import ( HTTPStatus, Response, abort, set_max_request_size, with_pool, with_transaction) diff --git a/modules/ldap_authentication/res.py b/modules/ldap_authentication/res.py index 32612083f5..3319780522 100644 --- a/modules/ldap_authentication/res.py +++ b/modules/ldap_authentication/res.py @@ -7,7 +7,7 @@ import urllib.parse import ldap3 from ldap3.core.exceptions import LDAPException -from trytond.config import config, parse_uri +import trytond.config as config from trytond.exceptions import LoginException from trytond.i18n import gettext from trytond.model.exceptions import AccessError @@ -24,7 +24,7 @@ if 'ldap' not in urllib.parse.uses_query: def parse_ldap_url(uri): unquote = urllib.parse.unquote - uri = parse_uri(uri) + uri = config.parse_uri(uri) dn = unquote(uri.path)[1:] attributes, scope, filter_, extensions = ( uri.query.split('?') + [''] * 4)[:4] diff --git a/modules/ldap_authentication/tests/test_module.py b/modules/ldap_authentication/tests/test_module.py index 3e6e0357ca..106872107f 100644 --- a/modules/ldap_authentication/tests/test_module.py +++ b/modules/ldap_authentication/tests/test_module.py @@ -7,7 +7,7 @@ from unittest.mock import ANY, MagicMock, Mock, patch import ldap3 -from trytond.config import config +import trytond.config as config from trytond.modules.ldap_authentication.res import ldap_server, parse_ldap_url from trytond.pool import Pool from trytond.tests.test_tryton import ModuleTestCase, with_transaction diff --git a/modules/marketing_automation/marketing_automation.py b/modules/marketing_automation/marketing_automation.py index 02b47a79d8..e888c4b2af 100644 --- a/modules/marketing_automation/marketing_automation.py +++ b/modules/marketing_automation/marketing_automation.py @@ -24,7 +24,7 @@ from genshi.template import TextTemplate from sql import Literal from sql.aggregate import Count -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ( EvalEnvironment, Index, ModelSQL, ModelView, Unique, Workflow, dualmethod, @@ -43,9 +43,6 @@ from trytond.wsgi import Base64Converter from .exceptions import ConditionError, DomainError, TemplateError from .mixin import MarketingAutomationMixin -USE_SSL = bool(config.get('ssl', 'certificate')) -URL_BASE = config.get('marketing', 'automation_base', default=http_host()) -URL_OPEN = urljoin(URL_BASE, '/m/empty.gif') logger = logging.getLogger(__name__) @@ -646,6 +643,9 @@ class Activity( WebShortener = pool.get('web.shortened_url') Email = pool.get('ir.email') record = record_activity.record + url_base = config.get( + 'marketing', 'automation_base', default=http_host()) + url_open = urljoin(url_base, '/m/empty.gif') with Transaction().set_context(language=record.language): record = record.__class__(record.id) @@ -655,7 +655,7 @@ class Activity( def unsubscribe(redirect): parts = urlsplit(urljoin( - URL_BASE, quote('/m/%(database)s/unsubscribe' % { + url_base, quote('/m/%(database)s/unsubscribe' % { 'database': Base64Converter(None).to_url( Transaction().database.name), }))) @@ -691,7 +691,7 @@ class Activity( elif kind is END and data == 'body': yield START, (QName('img'), Attrs([ (QName('src'), short( - URL_OPEN, 'on_email_opened')), + url_open, 'on_email_opened')), (QName('height'), '1'), (QName('width'), '1'), ])), pos diff --git a/modules/marketing_email/marketing.py b/modules/marketing_email/marketing.py index 13c739c2f9..2fb5f3641e 100644 --- a/modules/marketing_email/marketing.py +++ b/modules/marketing_email/marketing.py @@ -19,7 +19,7 @@ try: except ImportError: html2text = None -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.ir.session import token_hex from trytond.model import ( @@ -38,9 +38,6 @@ from trytond.wizard import Button, StateTransition, StateView, Wizard from .exceptions import EMailValidationError, TemplateError -URL_BASE = config.get('marketing', 'email_base', default=http_host()) -URL_OPEN = urljoin(URL_BASE, '/m/empty.gif') - def _add_params(url, **params): parts = urlsplit(url) @@ -434,6 +431,9 @@ class Message(Workflow, ModelSQL, ModelView): spy_pixel = config.getboolean( 'marketing', 'email_spy_pixel', default=False) + url_base = config.get('marketing', 'email_base', default=http_host()) + url_open = urljoin(url_base, '/m/empty.gif') + @lru_cache(None) def short(url, record): url = WebShortener( @@ -456,7 +456,7 @@ class Message(Workflow, ModelSQL, ModelView): elif kind is END and data == 'body' and spy_pixel: yield START, (QName('img'), Attrs([ (QName('src'), short( - URL_OPEN, str(message))), + url_open, str(message))), (QName('height'), '1'), (QName('width'), '1'), ])), pos diff --git a/modules/marketing_email/tests/test_module.py b/modules/marketing_email/tests/test_module.py index 0fbc63df42..40a27e76e1 100644 --- a/modules/marketing_email/tests/test_module.py +++ b/modules/marketing_email/tests/test_module.py @@ -3,7 +3,7 @@ from unittest.mock import ANY, Mock, patch -from trytond.config import config +import trytond.config as config from trytond.modules.marketing_email import marketing as marketing_module from trytond.pool import Pool from trytond.tests.test_tryton import ModuleTestCase, with_transaction @@ -26,21 +26,19 @@ class MarketingEmailTestCase(ModuleTestCase): 'marketing', 'email_subscribe_url', default='') config.set('marketing', 'email_subscribe_url', SUBSCRIBE_URL) self.addCleanup( - lambda: config.set( - 'marketing', 'email_subscribe_url', subscribe_url)) + config.set, 'marketing', 'email_subscribe_url', subscribe_url) unsubscribe_url = config.get( 'marketing', 'email_unsubscribe_url', default='') config.set('marketing', 'email_unsubscribe_url', UNSUBSCRIBE_URL) self.addCleanup( - lambda: config.set( - 'marketing', 'email_unsubscribe_url', unsubscribe_url)) + config.set, 'marketing', 'email_unsubscribe_url', unsubscribe_url) spy_pixel = config.get('marketing', 'email_spy_pixel', default='') config.set('marketing', 'email_spy_pixel', 'true') self.addCleanup( - lambda: config.set('marketing', 'email_spy_pixel', spy_pixel)) + config.set, 'marketing', 'email_spy_pixel', spy_pixel) from_ = config.get('email', 'from', default='') config.set('email', 'from', FROM) - self.addCleanup(lambda: config.set('email', 'from', from_)) + self.addCleanup(config.set, 'email', 'from', from_) @with_transaction() def test_subscribe(self): diff --git a/modules/notification_email/ir.py b/modules/notification_email/ir.py index cd1f132f20..9ae45ad6ba 100644 --- a/modules/notification_email/ir.py +++ b/modules/notification_email/ir.py @@ -3,8 +3,7 @@ from sql import Table -from trytond import backend -from trytond.config import config +from trytond import backend, config from trytond.model import fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval diff --git a/modules/notification_email/notification.py b/modules/notification_email/notification.py index 983fcc4c1f..38980d170a 100644 --- a/modules/notification_email/notification.py +++ b/modules/notification_email/notification.py @@ -5,7 +5,7 @@ from email.utils import getaddresses from genshi.template import TextTemplate -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ModelSQL, ModelView, fields from trytond.pool import Pool diff --git a/modules/notification_email/tests/test_module.py b/modules/notification_email/tests/test_module.py index a063061f21..5e02910c24 100644 --- a/modules/notification_email/tests/test_module.py +++ b/modules/notification_email/tests/test_module.py @@ -5,7 +5,7 @@ import sys import unittest from unittest.mock import patch -from trytond.config import config +import trytond.config as config try: from trytond.modules.company.tests import CompanyTestMixin diff --git a/modules/party_relationship/party.py b/modules/party_relationship/party.py index 19887421a2..c73869b120 100644 --- a/modules/party_relationship/party.py +++ b/modules/party_relationship/party.py @@ -8,14 +8,13 @@ from sql import As, Column, Literal, Null, Union, With from sql.aggregate import Min from sql.conditionals import Coalesce -from trytond.config import config +import trytond.config as config from trytond.model import Index, ModelSQL, ModelView, convert_from, fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval, If from trytond.transaction import Transaction dumps = partial(json.dumps, separators=(',', ':'), sort_keys=True) -default_depth = config.getint('party_relationship', 'depth', default=7) class RelationType(ModelSQL, ModelView): @@ -286,7 +285,9 @@ class Party(metaclass=PoolMeta): if party is None: party = context.get('related_party') if depth is None: - depth = context.get('depth', default_depth) + depth = context.get( + 'depth', + config.getint('party_relationship', 'depth', default=7)) if not party: return query diff --git a/modules/product/ir.py b/modules/product/ir.py index 799a83feae..03bbe4ab08 100644 --- a/modules/product/ir.py +++ b/modules/product/ir.py @@ -1,6 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.config import config + +import trytond.config as config from trytond.model import fields from trytond.pool import PoolMeta diff --git a/modules/product/uom.py b/modules/product/uom.py index dba6752b27..ea78bb4bb8 100644 --- a/modules/product/uom.py +++ b/modules/product/uom.py @@ -4,7 +4,7 @@ from decimal import Decimal from math import ceil, floor, log10 -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ( Check, DeactivableMixin, DigitsMixin, ModelSQL, ModelView, SymbolMixin, diff --git a/modules/product_cost_fifo/product.py b/modules/product_cost_fifo/product.py index 612d469dd4..2f8128ba84 100644 --- a/modules/product_cost_fifo/product.py +++ b/modules/product_cost_fifo/product.py @@ -3,7 +3,7 @@ import datetime as dt from decimal import Decimal -from trytond.config import config +import trytond.config as config from trytond.modules.product import round_price from trytond.pool import Pool, PoolMeta diff --git a/modules/product_image/product.py b/modules/product_image/product.py index f1a7d699dc..b266182b8c 100644 --- a/modules/product_image/product.py +++ b/modules/product_image/product.py @@ -7,7 +7,7 @@ from urllib.parse import quote, urlencode, urljoin import PIL import PIL.Image -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ( MatchMixin, ModelSQL, ModelView, Unique, fields, sequence_ordered) @@ -27,8 +27,6 @@ else: file_id = None store_prefix = None SIZE_MAX = config.getint('product', 'image_size_max', default=2048) -URL_BASE = config.get('product', 'image_base', default='') -URL_EXTERNAL_BASE = config.get('product', 'image_base', default=http_host()) class ImageURLMixin: @@ -68,8 +66,12 @@ class ImageURLMixin: return url def get_image_url(self, _external=False, **args): + url_base = config.get( + 'product', 'image_base', default='') + url_external_base = config.get( + 'product', 'image_base', default=http_host()) return self._image_url( - URL_EXTERNAL_BASE if _external else URL_BASE, **args) + url_external_base if _external else url_base, **args) @property def images_used(self): diff --git a/modules/product_image/routes.py b/modules/product_image/routes.py index be4a043233..a6b7e9acb0 100644 --- a/modules/product_image/routes.py +++ b/modules/product_image/routes.py @@ -2,13 +2,11 @@ # this repository contains the full copyright notices and license terms. from urllib.parse import unquote -from trytond.config import config +import trytond.config as config from trytond.protocols.wrappers import ( HTTPStatus, Response, abort, with_pool, with_transaction) from trytond.wsgi import app -TIMEOUT = config.getint('product', 'image_timeout', default=365 * 24 * 60 * 60) - @app.route( '/product/image//', @@ -92,6 +90,7 @@ def _image(request, pool, record): response = Response(image.get((width, height)), mimetype='image/jpeg') response.headers['Cache-Control'] = ( - 'max-age=%s, public' % TIMEOUT) + 'max-age=%s, public' % config.getint( + 'product', 'image_timeout', default=365 * 24 * 60 * 60)) response.add_etag() return response diff --git a/modules/product_price_list_dates/product.py b/modules/product_price_list_dates/product.py index 8d167e92df..9c549f89cb 100644 --- a/modules/product_price_list_dates/product.py +++ b/modules/product_price_list_dates/product.py @@ -3,15 +3,12 @@ import datetime as dt -from trytond.config import config +import trytond.config as config from trytond.model import ModelView, fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Date, Eval, If, PYSONDecoder, PYSONEncoder from trytond.transaction import Transaction -_cache_days = config.getint( - 'product_price_list_dates', 'cache_days', default=2) - class PriceList(metaclass=PoolMeta): __name__ = 'product.price_list' @@ -63,7 +60,8 @@ class PriceListCache(metaclass=PoolMeta): for pattern in super().patterns(price_list, product): if pattern is None: pattern = {} - for days in range(_cache_days): + for days in range(config.getint( + 'product_price_list_dates', 'cache_days', default=2)): pattern['date'] = today + dt.timedelta(days=days) yield pattern diff --git a/modules/sale_gift_card/sale.py b/modules/sale_gift_card/sale.py index 35a0419b2a..1fee1058f1 100644 --- a/modules/sale_gift_card/sale.py +++ b/modules/sale_gift_card/sale.py @@ -5,7 +5,7 @@ from sql import Null from sql.functions import CharLength from sql.operators import Equal -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import Exclude, ModelSQL, ModelView, Workflow, fields from trytond.modules.company.model import CompanyValueMixin diff --git a/modules/stock_package_shipping/stock.py b/modules/stock_package_shipping/stock.py index 1d426eb5df..34651d9ef5 100644 --- a/modules/stock_package_shipping/stock.py +++ b/modules/stock_package_shipping/stock.py @@ -2,7 +2,7 @@ # this repository contains the full copyright notices and license terms. import mimetypes -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ModelView, Workflow, fields from trytond.model.exceptions import AccessError diff --git a/modules/stock_package_shipping_dpd/configuration.py b/modules/stock_package_shipping_dpd/configuration.py index 07a38e994f..d323157610 100644 --- a/modules/stock_package_shipping_dpd/configuration.py +++ b/modules/stock_package_shipping_dpd/configuration.py @@ -5,7 +5,7 @@ from urllib.parse import urljoin from zeep import Client from zeep.transports import Transport -from trytond.config import config +import trytond.config as config SERVER_URLS = { 'testing': 'https://public-ws-stage.dpd.com/services/', @@ -14,17 +14,17 @@ SERVER_URLS = { LOGIN_SERVICE = 'LoginService/V2_0/?wsdl' SHIPMENT_SERVICE = 'ShipmentService/V3_2/?wsdl' -TIMEOUT = config.get( - 'stock_package_shipping_dpd', 'requests_timeout', default=300) def get_client(server, service): api_base_url = config.get('stock_package_shipping_dpd', server, default=SERVER_URLS[server]) url = urljoin(api_base_url, service) + timeout = config.get( + 'stock_package_shipping_dpd', 'requests_timeout', default=300) # Disable the cache for testing because zeep's bug # https://github.com/mvantellingen/python-zeep/issues/48 # which makes testing environments fail - transport = (Transport(cache=None, operation_timeout=TIMEOUT) + transport = (Transport(cache=None, operation_timeout=timeout) if url.startswith(SERVER_URLS['testing']) else None) return Client(url, transport=transport) diff --git a/modules/stock_package_shipping_mygls/api.py b/modules/stock_package_shipping_mygls/api.py index 33e14ef77c..c9040c1b35 100644 --- a/modules/stock_package_shipping_mygls/api.py +++ b/modules/stock_package_shipping_mygls/api.py @@ -5,14 +5,12 @@ import hashlib from zeep import Client from zeep.transports import Transport -from trytond.config import config +import trytond.config as config URLS = { 'testing': 'https://api.test.mygls.%(country)s/%(service)s.svc?singleWsdl', 'production': 'https://api.mygls.%(country)s/%(service)s.svc?singleWsdl', } -TIMEOUT = config.get( - 'stock_package_shipping_mygls', 'requests_timeout', default=300) def get_client(credential, service): @@ -20,7 +18,9 @@ def get_client(credential, service): 'country': credential.country, 'service': service, } - return Client(url, transport=Transport(operation_timeout=TIMEOUT)) + timeout = config.get( + 'stock_package_shipping_mygls', 'requests_timeout', default=300) + return Client(url, transport=Transport(operation_timeout=timeout)) def get_request(credential, name, **kwargs): diff --git a/modules/stock_package_shipping_sendcloud/carrier.py b/modules/stock_package_shipping_sendcloud/carrier.py index 4e4e5c7f1b..db19a5f0a6 100644 --- a/modules/stock_package_shipping_sendcloud/carrier.py +++ b/modules/stock_package_shipping_sendcloud/carrier.py @@ -5,8 +5,8 @@ from functools import wraps import requests +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.i18n import gettext from trytond.model import ( MatchMixin, ModelSQL, ModelView, fields, sequence_ordered) @@ -17,8 +17,6 @@ from trytond.pyson import Eval, If from .exceptions import SendcloudCredentialWarning, SendcloudError SENDCLOUD_API_URL = 'https://panel.sendcloud.sc/api/v2/' -TIMEOUT = config.getfloat( - 'stock_package_shipping_sendcloud', 'requests_timeout', default=300) HEADERS = { 'Sendcloud-Partner-Id': '03c1facb-63da-4bb1-889c-192fc91ec4e6', } @@ -88,9 +86,12 @@ class CredentialSendcloud(sequence_ordered(), ModelSQL, ModelView, MatchMixin): addresses = self._addresses_sender_cache.get(self.id) if addresses is not None: return addresses + timeout = config.getfloat( + 'stock_package_shipping_sendcloud', 'requests_timeout', + default=300) response = requests.get( SENDCLOUD_API_URL + 'user/addresses/sender', - auth=self.auth, timeout=TIMEOUT, headers=HEADERS) + auth=self.auth, timeout=timeout, headers=HEADERS) response.raise_for_status() addresses = response.json()['sender_addresses'] self._addresses_sender_cache.set(self.id, addresses) @@ -122,9 +123,12 @@ class CredentialSendcloud(sequence_ordered(), ModelSQL, ModelView, MatchMixin): params['service_point'] = service_point if is_return: params['is_return'] = is_return + timeout = config.getfloat( + 'stock_package_shipping_sendcloud', 'requests_timeout', + default=300) response = requests.get( SENDCLOUD_API_URL + 'shipping_methods', params=params, - auth=self.auth, timeout=TIMEOUT, headers=HEADERS) + auth=self.auth, timeout=timeout, headers=HEADERS) response.raise_for_status() methods = response.json()['shipping_methods'] self._shiping_methods_cache.set(key, methods) @@ -159,17 +163,23 @@ class CredentialSendcloud(sequence_ordered(), ModelSQL, ModelView, MatchMixin): @sendcloud_api def get_parcel(self, id): + timeout = config.getfloat( + 'stock_package_shipping_sendcloud', 'requests_timeout', + default=300) response = requests.get( SENDCLOUD_API_URL + 'parcels/%s' % id, - auth=self.auth, timeout=TIMEOUT, headers=HEADERS) + auth=self.auth, timeout=timeout, headers=HEADERS) response.raise_for_status() return response.json()['parcel'] @sendcloud_api def create_parcels(self, parcels): + timeout = config.getfloat( + 'stock_package_shipping_sendcloud', 'requests_timeout', + default=300) response = requests.post( SENDCLOUD_API_URL + 'parcels', json={'parcels': parcels}, - auth=self.auth, timeout=TIMEOUT, headers=HEADERS) + auth=self.auth, timeout=timeout, headers=HEADERS) if response.status_code == 400: msg = response.json()['error']['message'] raise requests.HTTPError(msg, response=response) @@ -178,8 +188,11 @@ class CredentialSendcloud(sequence_ordered(), ModelSQL, ModelView, MatchMixin): @sendcloud_api def get_label(self, url): + timeout = config.getfloat( + 'stock_package_shipping_sendcloud', 'requests_timeout', + default=300) response = requests.get( - url, auth=self.auth, timeout=TIMEOUT, headers=HEADERS) + url, auth=self.auth, timeout=timeout, headers=HEADERS) response.raise_for_status() return response.content diff --git a/modules/stock_package_shipping_ups/stock.py b/modules/stock_package_shipping_ups/stock.py index 8bd75e3fee..5233e4e167 100644 --- a/modules/stock_package_shipping_ups/stock.py +++ b/modules/stock_package_shipping_ups/stock.py @@ -10,7 +10,7 @@ from math import ceil import requests -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import fields from trytond.model.exceptions import AccessError @@ -23,8 +23,6 @@ from trytond.wizard import StateAction, StateTransition, Wizard from .exceptions import UPSError TRACKING_URL = 'https://www.ups.com/track' -TIMEOUT = config.getfloat( - 'stock_package_shipping_ups', 'requests_timeout', default=300) class PackageType(metaclass=PoolMeta): @@ -166,12 +164,14 @@ class CreateShippingUPS(Wizard): } nb_tries, response = 0, None error_message = '' + timeout = config.getfloat( + 'stock_package_shipping_ups', 'requests_timeout', default=300) try: while nb_tries < 5 and response is None: try: response = requests.post( api_url, json=shipment_request, headers=headers, - timeout=TIMEOUT) + timeout=timeout) except ssl.SSLError as e: error_message = e.reason nb_tries += 1 diff --git a/modules/timesheet_cost/company.py b/modules/timesheet_cost/company.py index 97a379cad3..57c2fc2dbd 100644 --- a/modules/timesheet_cost/company.py +++ b/modules/timesheet_cost/company.py @@ -2,8 +2,8 @@ # this repository contains the full copyright notices and license terms. from decimal import Decimal +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.model import Index, ModelSQL, ModelView, Unique, fields from trytond.pool import Pool, PoolMeta from trytond.transaction import Transaction diff --git a/modules/web_shop_product_data_feed/web.py b/modules/web_shop_product_data_feed/web.py index e41d007f40..d6a8d96021 100644 --- a/modules/web_shop_product_data_feed/web.py +++ b/modules/web_shop_product_data_feed/web.py @@ -10,7 +10,7 @@ import time from functools import wraps from string import Template -from trytond.config import config +import trytond.config as config from trytond.model import fields from trytond.pool import Pool, PoolMeta from trytond.transaction import Transaction @@ -82,6 +82,8 @@ class Shop(metaclass=PoolMeta): shops = cls.search([]) directory = _get_directory() + duration = config.getint( + 'web_shop', 'product_data_feed_cache', default=60 * 60 * 24) for shop in shops: for name in fnmatch.filter( os.listdir(directory), @@ -92,13 +94,16 @@ class Shop(metaclass=PoolMeta): else: format, language = name, None shop.product_data_feed_csv( - format, language=language, duration=_duration / 2) + format, language=language, duration=duration / 2) - def product_data_feed_csv(self, format, language=None, duration=_duration): + def product_data_feed_csv(self, format, language=None, duration=None): pool = Pool() Lang = pool.get('ir.lang') transaction = Transaction() directory = _get_directory() + if duration is None: + duration = config.getint( + 'web_shop', 'product_data_feed_cache', default=60 * 60 * 24) if language: filename = f'{self.name}-{self.id}-{format}-{language}.csv' else: diff --git a/modules/web_shop_shopify/web.py b/modules/web_shop_shopify/web.py index 33f556e408..fcca8057c0 100644 --- a/modules/web_shop_shopify/web.py +++ b/modules/web_shop_shopify/web.py @@ -10,8 +10,8 @@ import pyactiveresource import shopify from shopify.api_version import ApiVersion +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.i18n import gettext from trytond.model import ( MatchMixin, ModelSQL, ModelView, Unique, fields, sequence_ordered) diff --git a/modules/web_shortener/web.py b/modules/web_shortener/web.py index 8d3b357631..61bb60d9c0 100644 --- a/modules/web_shortener/web.py +++ b/modules/web_shortener/web.py @@ -7,7 +7,7 @@ from urllib.parse import quote, urljoin from sql.aggregate import Count -from trytond.config import config +import trytond.config as config from trytond.model import ModelSQL, ModelView, fields from trytond.pool import Pool from trytond.tools import grouped_slice, reduce_ids @@ -16,7 +16,6 @@ from trytond.url import http_host from trytond.wsgi import Base64Converter ALPHABET = string.digits + string.ascii_lowercase -URL_BASE = config.get('web', 'shortener_base', default=http_host()) logger = logging.getLogger(__name__) @@ -38,10 +37,11 @@ class ShortenedURL(ModelSQL, ModelView): 'database': Base64Converter(None).to_url( Transaction().database.name), } + url_base = config.get('web', 'shortener_base', default=http_host()) for shortened in shortened_urls: url_parts['short_id'] = cls._shorten(shortened.id) urls[shortened.id] = urljoin( - URL_BASE, quote('/s/%(database)s$%(short_id)s' % url_parts)) + url_base, quote('/s/%(database)s$%(short_id)s' % url_parts)) return urls @classmethod diff --git a/modules/web_user/tests/test_module.py b/modules/web_user/tests/test_module.py index 85de5726ce..4b53bd8778 100644 --- a/modules/web_user/tests/test_module.py +++ b/modules/web_user/tests/test_module.py @@ -3,7 +3,7 @@ from unittest.mock import patch -from trytond.config import config +import trytond.config as config from trytond.modules.party.tests import PartyCheckEraseMixin from trytond.modules.web_user import user as user_module from trytond.pool import Pool diff --git a/modules/web_user/user.py b/modules/web_user/user.py index 13104d20bd..f0508c1021 100644 --- a/modules/web_user/user.py +++ b/modules/web_user/user.py @@ -20,7 +20,7 @@ from sql.conditionals import Coalesce from sql.functions import CurrentTimestamp from sql.operators import Equal -from trytond.config import config +import trytond.config as config from trytond.exceptions import RateLimitException from trytond.i18n import gettext from trytond.model import ( diff --git a/proteus/proteus/config.py b/proteus/proteus/config.py index 45007ade37..dbaab9e6e6 100644 --- a/proteus/proteus/config.py +++ b/proteus/proteus/config.py @@ -264,7 +264,7 @@ class TrytondConfig(Config): os.environ['TRYTOND_DATABASE_URI'] = database if not config_file: config_file = os.environ.get('TRYTOND_CONFIG') - from trytond.config import config + import trytond.config as config config.update_etc(config_file) from trytond.pool import Pool from trytond.transaction import Transaction diff --git a/trytond-gis/trytond_gis/__init__.py b/trytond-gis/trytond_gis/__init__.py index 908fbabc58..a06fbd33ff 100644 --- a/trytond-gis/trytond_gis/__init__.py +++ b/trytond-gis/trytond_gis/__init__.py @@ -1,6 +1,6 @@ # This file is part of trytond_gis. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.config import config +import trytond.config as config from .const import WGS_84 diff --git a/trytond-gis/trytond_gis/postgis/table.py b/trytond-gis/trytond_gis/postgis/table.py index b51fc5aeea..bbb1ea185c 100644 --- a/trytond-gis/trytond_gis/postgis/table.py +++ b/trytond-gis/trytond_gis/postgis/table.py @@ -2,8 +2,8 @@ # this repository contains the full copyright notices and license terms. import logging +import trytond.config as config from trytond.backend.postgresql.table import TableHandler as PGTableHandler -from trytond.config import config from trytond.transaction import Transaction from trytond_gis.const import GIS_SQL_TYPE_RE, WGS_84 diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 0058ea2397..fb15bf2180 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Refactor of trytond.config to add cache * Handle URL host of the bus on login * Restrict sessions to the client's IP address * Use ``tryton.cfg`` to register in the Pool diff --git a/trytond/bin/trytond b/trytond/bin/trytond index 3097f60118..3210d25e69 100755 --- a/trytond/bin/trytond +++ b/trytond/bin/trytond @@ -19,7 +19,7 @@ if os.path.isdir(DIR): sys.path.insert(0, os.path.dirname(DIR)) import trytond.commandline as commandline -from trytond.config import config, split_netloc +import trytond.config as config parser = commandline.get_parser_daemon() if argcomplete: @@ -47,7 +47,7 @@ with commandline.pidfile(options): threads.append(thread) for thread in threads: thread.join() - hostname, port = split_netloc(config.get('web', 'listen')) + hostname, port = config.split_netloc(config.get('web', 'listen')) certificate = config.get('ssl', 'certificate') try: if config.getboolean('ssl', 'certificate'): diff --git a/trytond/bin/trytond-admin b/trytond/bin/trytond-admin index 8ae76e1c07..cf660cafc3 100755 --- a/trytond/bin/trytond-admin +++ b/trytond/bin/trytond-admin @@ -16,7 +16,7 @@ if os.path.isdir(DIR): sys.path.insert(0, os.path.dirname(DIR)) import trytond.commandline as commandline -from trytond.config import config +import trytond.config as config parser = commandline.get_parser_admin() if argcomplete: diff --git a/trytond/bin/trytond-console b/trytond/bin/trytond-console index 6ec5f00c84..1276a3344a 100755 --- a/trytond/bin/trytond-console +++ b/trytond/bin/trytond-console @@ -16,7 +16,7 @@ if os.path.isdir(DIR): sys.path.insert(0, os.path.dirname(DIR)) import trytond.commandline as commandline -from trytond.config import config +import trytond.config as config parser = commandline.get_parser_console() if argcomplete: diff --git a/trytond/bin/trytond-cron b/trytond/bin/trytond-cron index d7e87760b2..91741d1e05 100755 --- a/trytond/bin/trytond-cron +++ b/trytond/bin/trytond-cron @@ -16,7 +16,7 @@ if os.path.isdir(DIR): sys.path.insert(0, os.path.dirname(DIR)) import trytond.commandline as commandline -from trytond.config import config +import trytond.config as config parser = commandline.get_parser_cron() if argcomplete: diff --git a/trytond/bin/trytond-stat b/trytond/bin/trytond-stat index 528c296cee..ea390e80ed 100755 --- a/trytond/bin/trytond-stat +++ b/trytond/bin/trytond-stat @@ -20,7 +20,7 @@ if os.path.isdir(DIR): sys.path.insert(0, os.path.dirname(DIR)) import trytond.commandline as commandline -from trytond.config import config +import trytond.config as config parser = commandline.get_parser_stat() if argcomplete: diff --git a/trytond/bin/trytond-worker b/trytond/bin/trytond-worker index 031816bc1e..48baa04415 100755 --- a/trytond/bin/trytond-worker +++ b/trytond/bin/trytond-worker @@ -16,7 +16,7 @@ if os.path.isdir(DIR): sys.path.insert(0, os.path.dirname(DIR)) import trytond.commandline as commandline -from trytond.config import config +import trytond.config as config parser = commandline.get_parser_worker() if argcomplete: diff --git a/trytond/doc/ref/config.rst b/trytond/doc/ref/config.rst new file mode 100644 index 0000000000..f8f89ad433 --- /dev/null +++ b/trytond/doc/ref/config.rst @@ -0,0 +1,81 @@ +.. _ref-config: +.. module:: trytond.config + +====== +Config +====== + +The config gives access to the configuration. + +.. contents:: + :local: + :backlinks: entry + :depth: 1 + +.. function:: get_hostname(netloc) + + Returns the hostname from the network location/authority of :abbr:`URI + (Uniform Resource Identifier)`. + +.. function:: get_port(netloc) + + Returns the port from the network location/authority of :abbr:`URI (Uniform + Resource Identifier)`. + +.. function:: split_netloc(netloc) + + Returns the couple hostname and port from the network location/authority of + :abbr:`URI (Uniform Resource Identifier)`. + +.. function:: parse_listen(value) + + Yields couples of hostname and port from the comma separated list of network + location/authority of :abbr:`URI (Uniform Resource Identifier)`. + +.. function:: parse_uri(uri) + + Parses a :abbr:`URI (Uniform Resource Identifier)`, returning a namedtuple. + +.. function:: update_etc([configfile]) + + Tries to update the configuration with the file or list of files and returns + the filenames which were successfully parsed. + +.. function:: has_section(section) + + Indicates whether the named section is present in the configuration. + +.. function:: add_section(section) + + Adds the named section. + +.. function:: remove_section(section) + + Removes the named section. + +.. function:: set(section, option[, value]) + + Set the given option to the specified value. + +.. function:: get(section, option[, default]) + + Get an option value for the named section. If the key is not found the + default value is provided. + +.. function:: getint(section, option[, default]) + + A convenience method which coerces the option in the specified section to an + :py:class:`int`. + See :func:`get`. + +.. function:: getfloat(section, option[, default]) + + A convenience method which coerces the option in the specified section to a + :py:class:`float`. + See :func:`get`. + +.. function:: getboolean(section, option[, default]) + + A convenience method which coerces the option in the specified section to a + :py:class:`bool`. + See :func:`get`. diff --git a/trytond/doc/ref/index.rst b/trytond/doc/ref/index.rst index ee1fd1cfd9..b34b634725 100644 --- a/trytond/doc/ref/index.rst +++ b/trytond/doc/ref/index.rst @@ -9,6 +9,7 @@ Reference of the ``trytond`` API: .. toctree:: :maxdepth: 1 + config models fields wizard diff --git a/trytond/trytond/admin.py b/trytond/trytond/admin.py index a6160426c4..b352af2834 100644 --- a/trytond/trytond/admin.py +++ b/trytond/trytond/admin.py @@ -8,8 +8,7 @@ from getpass import getpass from sql import Literal, Table -from trytond import __series__, backend -from trytond.config import config +from trytond import __series__, backend, config from trytond.pool import Pool from trytond.sendmail import send_test_email from trytond.tools import file_open, find_path diff --git a/trytond/trytond/backend/__init__.py b/trytond/trytond/backend/__init__.py index 6dde7d5b99..3db79b28e2 100644 --- a/trytond/trytond/backend/__init__.py +++ b/trytond/trytond/backend/__init__.py @@ -8,7 +8,7 @@ try: except ImportError: from importlib.metadata import entry_points -from trytond.config import config +import trytond.config as config __all__ = [ 'name', 'Database', 'TableHandler', diff --git a/trytond/trytond/backend/postgresql/database.py b/trytond/trytond/backend/postgresql/database.py index 2692207d6a..f59b2cc9dc 100644 --- a/trytond/trytond/backend/postgresql/database.py +++ b/trytond/trytond/backend/postgresql/database.py @@ -36,9 +36,8 @@ from sql.conditionals import Coalesce from sql.functions import Function from sql.operators import BinaryOperator, Concat -from trytond import __series__ +from trytond import __series__, config from trytond.backend.database import DatabaseInterface, SQLType -from trytond.config import config, parse_uri from trytond.sql.operators import RangeOperator from trytond.tools import grouped_slice, reduce_ids from trytond.tools.gevent import is_gevent_monkey_patched @@ -53,9 +52,6 @@ __all__ = [ logger = logging.getLogger(__name__) os.environ['PGTZ'] = os.environ.get('TZ', '') -_timeout = config.getint('database', 'timeout') -_minconn = config.getint('database', 'minconn', default=1) -_maxconn = config.getint('database', 'maxconn', default=64) _default_name = config.get('database', 'default_name', default='template1') @@ -224,8 +220,9 @@ class Database(DatabaseInterface): with cls._lock: now = datetime.now() databases = cls._databases[os.getpid()] + timeout = config.getint('database', 'timeout') for database in list(databases.values()): - if ((now - database._last_use).total_seconds() > _timeout + if ((now - database._last_use).total_seconds() > timeout and database.name != name and not database._connpool._used): database.close() @@ -233,9 +230,11 @@ class Database(DatabaseInterface): inst = databases[name] else: inst = DatabaseInterface.__new__(cls, name=name) + minconn = config.getint('database', 'minconn', default=1) + maxconn = config.getint('database', 'maxconn', default=64) try: inst._connpool = ThreadedConnectionPool( - _minconn, _maxconn, **cls._connection_params(name), + minconn, maxconn, **cls._connection_params(name), cursor_factory=LoggingCursor) except Exception: logger.error( @@ -252,7 +251,7 @@ class Database(DatabaseInterface): @classmethod def _connection_params(cls, name): - uri = parse_uri(config.get('database', 'uri')) + uri = config.parse_uri(config.get('database', 'uri')) if uri.path and uri.path != '/': warnings.warn("The path specified in the URI will be overridden") params = { @@ -267,7 +266,9 @@ class Database(DatabaseInterface): def get_connection( self, autocommit=False, readonly=False, statement_timeout=None): - retry = max(config.getint('database', 'retry'), _maxconn) + retry = max( + config.getint('database', 'retry'), + config.getint('database', 'maxconn', default=64)) for count in range(retry, -1, -1): try: conn = self._connpool.getconn() diff --git a/trytond/trytond/backend/sqlite/database.py b/trytond/trytond/backend/sqlite/database.py index c9a5c4a791..ddd92428a3 100644 --- a/trytond/trytond/backend/sqlite/database.py +++ b/trytond/trytond/backend/sqlite/database.py @@ -24,9 +24,8 @@ from sql.functions import ( Substring, Trim) from sql.operators import Equal -from trytond import __series__ +from trytond import __series__, config from trytond.backend.database import DatabaseInterface, SQLType -from trytond.config import config, parse_uri from trytond.tools import safe_join from trytond.transaction import Transaction @@ -539,7 +538,7 @@ class Database(DatabaseInterface): def _make_uri(self): uri = config.get('database', 'uri') - base_uri = parse_uri(uri) + base_uri = config.parse_uri(uri) if base_uri.path and base_uri.path != '/': warnings.warn("The path specified in the URI will be overridden") diff --git a/trytond/trytond/bus.py b/trytond/trytond/bus.py index 360e2299d1..32c606e4e5 100644 --- a/trytond/trytond/bus.py +++ b/trytond/trytond/bus.py @@ -10,8 +10,8 @@ import threading import time import uuid +import trytond.config as config from trytond import backend -from trytond.config import config from trytond.protocols.jsonrpc import JSONDecoder, JSONEncoder from trytond.protocols.wrappers import HTTPStatus, Response, abort, exceptions from trytond.tools import resolve @@ -20,14 +20,6 @@ from trytond.wsgi import app logger = logging.getLogger(__name__) -_db_timeout = config.getint('database', 'timeout') -_cache_timeout = config.getint('bus', 'cache_timeout') -_select_timeout = config.getint('bus', 'select_timeout') -_long_polling_timeout = config.getint('bus', 'long_polling_timeout') -_allow_subscribe = config.getboolean('bus', 'allow_subscribe') -_url_host = config.get('bus', 'url_host') -_web_cache_timeout = config.getint('web', 'cache_timeout') - class _MessageQueue: @@ -84,7 +76,8 @@ class LongPollingBus: pid = os.getpid() with cls._queues_lock[pid]: start_listener = (pid, database) not in cls._queues - cls._queues[pid, database]['timeout'] = time.time() + _db_timeout + cls._queues[pid, database]['timeout'] = ( + time.time() + config.getint('database', 'timeout')) if start_listener: listener = threading.Thread( target=cls._listen, args=(database,), daemon=True) @@ -106,7 +99,8 @@ class LongPollingBus: 'events'][channel] event_channel.append(event) - triggered = event.wait(_long_polling_timeout) + triggered = event.wait( + config.getint('bus', 'long_polling_timeout')) if not triggered: response = cls.create_response(None, None) else: @@ -153,12 +147,13 @@ class LongPollingBus: cursor = conn.cursor() cursor.execute('LISTEN "%s"' % cls._channel) - cls._messages[database] = messages = _MessageQueue(_cache_timeout) + cls._messages[database] = messages = _MessageQueue( + config.getint('bus', 'cache_timeout')) now = time.time() selector.register(conn, selectors.EVENT_READ) while cls._queues[pid, database]['timeout'] > now: - selector.select(timeout=_select_timeout) + selector.select(timeout=config.getint('bus', 'select_timeout')) conn.poll() while conn.notifies: notification = conn.notifies.pop() @@ -226,9 +221,10 @@ else: @app.route('//bus', methods=['POST']) @app.session_valid def subscribe(request, database_name): - if not _allow_subscribe: + if not config.getboolean('bus', 'allow_subscribe'): raise exceptions.NotImplemented - if _url_host and _url_host != request.host_url: + url_host = config.get('bus', 'url_host') + if url_host and url_host != request.host_url: abort(HTTPStatus.UNAUTHORIZED) user = request.authorization.get('userid') channels = request.parsed_data.get('channels', []) diff --git a/trytond/trytond/cache.py b/trytond/trytond/cache.py index a5754ec0fc..7a5a72ed7c 100644 --- a/trytond/trytond/cache.py +++ b/trytond/trytond/cache.py @@ -14,17 +14,13 @@ from sql import Conflict, Table from sql.aggregate import Max from sql.functions import CurrentTimestamp, Function -from trytond import backend -from trytond.config import config +from trytond import backend, config from trytond.pool import Pool from trytond.tools import grouped_slice, resolve from trytond.tools.multiprocessing import local from trytond.transaction import Transaction __all__ = ['BaseCache', 'Cache', 'LRUDict', 'LRUDictTransaction'] -_clean_timeout = config.getint('cache', 'clean_timeout') -_select_timeout = config.getint('cache', 'select_timeout') -_default_size_limit = config.getint('cache', 'default') logger = logging.getLogger(__name__) REFRESH_POOL_MSG = "refresh pool" @@ -90,7 +86,7 @@ class BaseCache(object): f" in regards to context ({context}).") self._name = name self.size_limit = config.getint( - 'cache', name, default=_default_size_limit) + 'cache', name, default=config.getint('cache', 'default')) self.context = context self.context_ignored_keys = set() if context and context_ignored_keys: @@ -247,7 +243,8 @@ class MemoryCache(BaseCache): database = transaction.database dbname = database.name - if not _clean_timeout and database.has_channel(): + clean_timeout = config.getint('cache', 'clean_timeout') + if not clean_timeout and database.has_channel(): with cls._local.listener_lock: if dbname not in cls._local.listeners: listener = threading.Thread( @@ -256,7 +253,7 @@ class MemoryCache(BaseCache): listener.start() return last_clean = (dt.datetime.now() - cls._clean_last).total_seconds() - if last_clean < _clean_timeout: + if last_clean < clean_timeout: return connection = database.get_connection(readonly=True, autocommit=True) try: @@ -292,7 +289,8 @@ class MemoryCache(BaseCache): return database = transaction.database dbname = database.name - if not _clean_timeout and transaction.database.has_channel(): + clean_timeout = config.getint('cache', 'clean_timeout') + if not clean_timeout and transaction.database.has_channel(): with transaction.connection.cursor() as cursor: # The count computed as # 8000 (max notify size) / 64 (max name data len) @@ -379,7 +377,8 @@ class MemoryCache(BaseCache): def refresh_pool(cls, transaction): database = transaction.database dbname = database.name - if not _clean_timeout and database.has_channel(): + clean_timeout = config.getint('cache', 'clean_timeout') + if not clean_timeout and database.has_channel(): database = backend.Database(dbname) conn = database.get_connection() process_id = cls._local.portable_id @@ -415,7 +414,8 @@ class MemoryCache(BaseCache): selector.register(conn, selectors.EVENT_READ) while cls._local.listeners.get(dbname) == current_thread: - selector.select(timeout=_select_timeout) + selector.select( + timeout=config.getint('cache', 'select_timeout')) conn.poll() while conn.notifies: notification = conn.notifies.pop() diff --git a/trytond/trytond/commandline.py b/trytond/trytond/commandline.py index 054ba6a087..17e9630b6f 100644 --- a/trytond/trytond/commandline.py +++ b/trytond/trytond/commandline.py @@ -16,7 +16,7 @@ logger = logging.getLogger(__name__) def database_completer(parsed_args, **kwargs): - from trytond.config import config + import trytond.config as config from trytond.transaction import Transaction config.update_etc(parsed_args.configfile) with Transaction().start( diff --git a/trytond/trytond/config.py b/trytond/trytond/config.py index c5cc9096a1..7d76212767 100644 --- a/trytond/trytond/config.py +++ b/trytond/trytond/config.py @@ -3,11 +3,17 @@ import configparser import logging import os +import sys import urllib.parse +import warnings +from functools import cache from getpass import getuser -__all__ = ['config', 'get_hostname', 'get_port', 'split_netloc', - 'parse_listen', 'parse_uri'] +__all__ = [ + 'get_hostname', 'get_port', 'split_netloc', + 'parse_listen', 'parse_uri', + 'update_etc', 'has_section', 'add_section', 'remove_section', + 'set', 'get', 'getint', 'getfloat', 'getboolean'] logger = logging.getLogger(__name__) # Needed so urlunsplit to always set netloc @@ -135,40 +141,61 @@ class TrytonConfigParser(configparser.ConfigParser): ','.join(set(configfile) - set(read_files))) return configfile - def get(self, section, option, *args, **kwargs): - default = kwargs.pop('default', None) - try: - return configparser.RawConfigParser.get(self, section, option, - *args, **kwargs) - except (configparser.NoOptionError, configparser.NoSectionError): - return default - def getint(self, section, option, *args, **kwargs): - default = kwargs.pop('default', None) - try: - return configparser.RawConfigParser.getint(self, section, option, - *args, **kwargs) - except (configparser.NoOptionError, configparser.NoSectionError, - TypeError): - return default - - def getfloat(self, section, option, *args, **kwargs): - default = kwargs.pop('default', None) - try: - return configparser.RawConfigParser.getfloat(self, section, option, - *args, **kwargs) - except (configparser.NoOptionError, configparser.NoSectionError, - TypeError): - return default - - def getboolean(self, section, option, *args, **kwargs): - default = kwargs.pop('default', None) - try: - return configparser.RawConfigParser.getboolean( - self, section, option, *args, **kwargs) - except (configparser.NoOptionError, configparser.NoSectionError, - AttributeError): - return default +_config = TrytonConfigParser() -config = TrytonConfigParser() +def update_etc(configfile=os.environ.get('TRYTOND_CONFIG')): + configfile = _config.update_etc(configfile=configfile) + _cache_clear() + return configfile + + +has_section = _config.has_section +add_section = _config.add_section +remove_section = _config.remove_section + + +def set(section, option, value=None): + _config.set(section, option, value=value) + _cache_clear() + + +@cache +def get(section, option, default=None): + return configparser.RawConfigParser.get( + _config, section, option, fallback=default) + + +def _cache_clear(): + get.cache_clear() + getint.cache_clear() + getfloat.cache_clear() + getboolean.cache_clear() + + +@cache +def getint(section, option, default=None): + return configparser.RawConfigParser.getint( + _config, section, option, fallback=default) + + +@cache +def getfloat(section, option, default=None): + return configparser.RawConfigParser.getfloat( + _config, section, option, fallback=default) + + +@cache +def getboolean(section, option, default=None): + return configparser.RawConfigParser.getboolean( + _config, section, option, fallback=default) + + +def __getattr__(name): + if name == 'config': + warnings.warn( + "trytond.config.config is deprecated, " + "use trytond.config", DeprecationWarning, stacklevel=2) + return sys.modules[__name__] + raise AttributeError(f"module {__name__} has no attribute {name}") diff --git a/trytond/trytond/filestore.py b/trytond/trytond/filestore.py index fa08aaca9b..26ef933023 100644 --- a/trytond/trytond/filestore.py +++ b/trytond/trytond/filestore.py @@ -2,13 +2,17 @@ # this repository contains the full copyright notices and license terms. import hashlib import os +from functools import cache -from trytond.config import config +import trytond.config as config from trytond.tools import resolve __all__ = ['filestore'] -PATH = os.path.normpath(config.get('database', 'path')) + +@cache +def _normpath(path): + return os.path.normpath(path) class FileStore(object): @@ -52,10 +56,15 @@ class FileStore(object): def setmany(self, data, prefix=''): return [self.set(d, prefix) for d in data] + @property + def path(self): + return _normpath(config.get('database', 'path')) + def _filename(self, id, prefix): - filename = os.path.join(PATH, prefix, id[0:2], id[2:4], id) + path = self.path + filename = os.path.join(path, prefix, id[0:2], id[2:4], id) filename = os.path.normpath(filename) - if not filename.startswith(PATH): + if not filename.startswith(path): raise ValueError('Bad prefix') return filename diff --git a/trytond/trytond/ir/attachment.py b/trytond/trytond/ir/attachment.py index 2659116784..30e895ee06 100644 --- a/trytond/trytond/ir/attachment.py +++ b/trytond/trytond/ir/attachment.py @@ -1,6 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.config import config + +import trytond.config as config from trytond.i18n import lazy_gettext from trytond.model import ModelSQL, ModelView, fields from trytond.pool import Pool diff --git a/trytond/trytond/ir/avatar.py b/trytond/trytond/ir/avatar.py index 5576d14a74..9be29ad9e3 100644 --- a/trytond/trytond/ir/avatar.py +++ b/trytond/trytond/ir/avatar.py @@ -13,7 +13,7 @@ try: except ImportError: PIL = None -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import ModelSQL, Unique, fields from trytond.model.exceptions import ValidationError @@ -29,7 +29,6 @@ if config.getboolean('database', 'avatar_filestore', default=False): else: file_id = None store_prefix = None -URL_BASE = config.get('web', 'avatar_base', default='') FONT = os.path.join(os.path.dirname(__file__), 'fonts', 'karla.ttf') @@ -89,8 +88,9 @@ class Avatar(ImageMixin, ResourceMixin, ModelSQL): @property def url(self): if self.image_id or self.image: + url_base = config.get('web', 'avatar_base', default='') return urljoin( - URL_BASE, quote('/avatar/%(database)s/%(uuid)s' % { + url_base, quote('/avatar/%(database)s/%(uuid)s' % { 'database': Base64Converter(None).to_url( Transaction().database.name), 'uuid': self.uuid, diff --git a/trytond/trytond/ir/configuration.py b/trytond/trytond/ir/configuration.py index 5c3a8f26e1..a803a1a017 100644 --- a/trytond/trytond/ir/configuration.py +++ b/trytond/trytond/ir/configuration.py @@ -1,7 +1,8 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. + +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.model import ModelSingleton, ModelSQL, fields diff --git a/trytond/trytond/ir/cron.py b/trytond/trytond/ir/cron.py index 9c65852e14..ca84f534a9 100644 --- a/trytond/trytond/ir/cron.py +++ b/trytond/trytond/ir/cron.py @@ -11,8 +11,7 @@ from dateutil.relativedelta import relativedelta from sql import Literal from sql.conditionals import Coalesce -from trytond import backend -from trytond.config import config +from trytond import backend, config from trytond.exceptions import UserError, UserWarning from trytond.model import ( DeactivableMixin, Index, ModelSQL, ModelView, dualmethod, fields) @@ -24,7 +23,6 @@ from trytond.tools import timezone as tz from trytond.transaction import Transaction, TransactionError from trytond.worker import run_task -clean_days = config.getint('cron', 'clean_days', default=30) logger = logging.getLogger(__name__) @@ -315,6 +313,7 @@ class Log(ModelSQL, ModelView): @classmethod def clean(cls, date=None): if date is None: + clean_days = config.getint('cron', 'clean_days', default=30) date = ( datetime.datetime.now() - datetime.timedelta(days=clean_days)) logs = cls.search([('create_date', '<', date)]) diff --git a/trytond/trytond/ir/email_.py b/trytond/trytond/ir/email_.py index 245a858834..d93e120eef 100644 --- a/trytond/trytond/ir/email_.py +++ b/trytond/trytond/ir/email_.py @@ -12,7 +12,7 @@ except ImportError: html2text = None from genshi.template import TextTemplate -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.model import EvalEnvironment, ModelSQL, ModelView, fields from trytond.model.exceptions import AccessError, ValidationError diff --git a/trytond/trytond/ir/error.py b/trytond/trytond/ir/error.py index b711ace0dc..db7b2c830c 100644 --- a/trytond/trytond/ir/error.py +++ b/trytond/trytond/ir/error.py @@ -5,7 +5,7 @@ import functools import logging import warnings -from trytond.config import config +import trytond.config as config from trytond.exceptions import UserError, UserWarning from trytond.model import ( Index, ModelSQL, ModelView, Workflow, dualmethod, fields) @@ -15,7 +15,6 @@ from trytond.tools import firstline from trytond.transaction import Transaction logger = logging.getLogger(__name__) -clean_days = config.getint('error', 'clean_days', default=90) def set_user(field): @@ -159,6 +158,7 @@ class Error(Workflow, ModelView, ModelSQL): @classmethod def clean(cls, date=None): if date is None: + clean_days = config.getint('error', 'clean_days', default=90) date = ( dt.datetime.now() - dt.timedelta(days=clean_days)) errors = cls.search([('create_date', '<', date)]) diff --git a/trytond/trytond/ir/model.py b/trytond/trytond/ir/model.py index 5b208532f0..f23aa25326 100644 --- a/trytond/trytond/ir/model.py +++ b/trytond/trytond/ir/model.py @@ -12,8 +12,8 @@ from sql.aggregate import Max from sql.conditionals import Case from sql.operators import Equal +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.i18n import gettext from trytond.model import ( DeactivableMixin, EvalEnvironment, Exclude, Index, ModelSingleton, @@ -32,7 +32,6 @@ from trytond.wizard import Button, StateAction, StateView, Wizard from .resource import ResourceAccessMixin logger = logging.getLogger(__name__) -_request_timeout = config.getint('request', 'timeout', default=0) class ConditionError(ValidationError): @@ -76,7 +75,8 @@ class Model( 'list_history': RPC(), 'get_notification': RPC(), 'get_names': RPC(), - 'global_search': RPC(timeout=_request_timeout), + 'global_search': RPC( + timeout=config.getint('request', 'timeout', default=0)), }) @classmethod diff --git a/trytond/trytond/ir/queue_.py b/trytond/trytond/ir/queue_.py index 398f431cd5..5dfd64c1d3 100644 --- a/trytond/trytond/ir/queue_.py +++ b/trytond/trytond/ir/queue_.py @@ -6,17 +6,13 @@ from sql import Literal, Null, With from sql.aggregate import Min from sql.functions import CurrentTimestamp, Extract -from trytond.config import config +import trytond.config as config from trytond.model import Index, ModelSQL, fields from trytond.pool import Pool from trytond.tools import grouped_slice from trytond.transaction import ( Transaction, inactive_records, without_check_access) -has_worker = config.getboolean('queue', 'worker', default=False) -clean_days = config.getint('queue', 'clean_days', default=30) -batch_size = config.getint('queue', 'batch_size', default=20) - class Queue(ModelSQL): __name__ = 'ir.queue' @@ -75,7 +71,7 @@ class Queue(ModelSQL): }]) if database.has_channel(): cursor.execute('NOTIFY "%s"', (cls.__name__,)) - if not has_worker: + if not config.getboolean('queue', 'worker', default=False): transaction.tasks.append(record.id) return record.id @@ -185,7 +181,8 @@ class Queue(ModelSQL): def clean(cls, date=None): if date is None: date = ( - datetime.datetime.now() - datetime.timedelta(days=clean_days)) + datetime.datetime.now() - datetime.timedelta( + days=config.getint('queue', 'clean_days', default=30))) tasks = cls.search(['OR', ('dequeued_at', '<', date), ('finished_at', '<', date), @@ -215,6 +212,7 @@ class _Method(object): def __call__(self, instances, *args, **kwargs): transaction = Transaction() context = transaction.context.copy() + has_worker = config.getboolean('queue', 'worker', default=False) name = context.pop('queue_name', 'default') now = datetime.datetime.now() scheduled_at = context.pop('queue_scheduled_at', None) @@ -248,7 +246,7 @@ class _Method(object): if isinstance(instances, list): if has_worker and queue_batch: if isinstance(queue_batch, bool): - count = batch_size + count = config.getint('queue', 'batch_size', default=20) else: count = int(queue_batch) else: diff --git a/trytond/trytond/ir/routes.py b/trytond/trytond/ir/routes.py index ceeaad508f..0b7cbd9fe9 100644 --- a/trytond/trytond/ir/routes.py +++ b/trytond/trytond/ir/routes.py @@ -6,7 +6,7 @@ import io import json from numbers import Number -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext from trytond.protocols.jsonrpc import JSONDecoder from trytond.protocols.wrappers import ( @@ -15,14 +15,6 @@ from trytond.tools import slugify from trytond.transaction import Transaction from trytond.wsgi import app -SOURCE = config.get( - 'html', 'src', - default='https://cdn.tiny.cloud/1/no-api-key/tinymce/7/tinymce.min.js') -LICENSE_KEY = config.get('html', 'license_key', default='gpl') -AVATAR_TIMEOUT = config.getint( - 'web', 'avatar_timeout', default=7 * 24 * 60 * 60) -_request_timeout = config.getint('request', 'timeout', default=0) - def get_token(record): return str((record.write_date or record.create_date).timestamp()) @@ -84,10 +76,14 @@ def html_editor(request, pool, model, record, field): 'field': field.string, 'title': request.args.get('title', "Tryton"), } - + source = config.get( + 'html', 'src', + default='https://cdn.tiny.cloud' + '/1/no-api-key/tinymce/7/tinymce.min.js') + license_key = config.get('html', 'license_key', default='gpl') return Response(TEMPLATE % { - 'source': SOURCE, - 'license_key': LICENSE_KEY, + 'source': source, + 'license_key': license_key, 'plugins': get_config( ['plugins', model, field.name], default=''), 'css': get_config( @@ -190,7 +186,8 @@ TEMPLATE = ''' @app.auth_required @with_pool @with_transaction( - user='request', context=dict(_check_access=True), timeout=_request_timeout) + user='request', context=dict(_check_access=True), + timeout=config.getint('request', 'timeout', default=0)) def data(request, pool, model): User = pool.get('res.user') Lang = pool.get('ir.lang') @@ -297,6 +294,7 @@ def avatar(request, pool, uuid): abort(HTTPStatus.BAD_REQUEST) response = Response(avatar.get(size), mimetype='image/jpeg') response.headers['Cache-Control'] = ( - 'max-age=%s, public' % AVATAR_TIMEOUT) + 'max-age=%s, public' % config.getint( + 'web', 'avatar_timeout', default=7 * 24 * 60 * 60)) response.add_etag() return response diff --git a/trytond/trytond/ir/session.py b/trytond/trytond/ir/session.py index 01d73cfea7..ad50b1fcd8 100644 --- a/trytond/trytond/ir/session.py +++ b/trytond/trytond/ir/session.py @@ -4,15 +4,11 @@ import datetime import json from secrets import compare_digest, token_hex +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.model import Index, ModelSQL, fields from trytond.transaction import Transaction -_session_timeout = datetime.timedelta( - seconds=config.getint('session', 'timeout')) -_reset_interval = _session_timeout // 10 - class Session(ModelSQL): __name__ = 'ir.session' @@ -135,8 +131,11 @@ class Session(ModelSQL): "Reset key session timestamp" now = datetime.datetime.now() last_reset = cls._session_reset_cache.get(key) - if last_reset is None or (now - _reset_interval) > last_reset: - timestamp = now - _session_timeout + session_timeout = datetime.timedelta( + seconds=config.getint('session', 'timeout')) + reset_interval = session_timeout // 10 + if last_reset is None or (now - reset_interval) > last_reset: + timestamp = now - session_timeout sessions = cls.search([ ('key', '=', key), ['OR', diff --git a/trytond/trytond/ir/translation.py b/trytond/trytond/ir/translation.py index eb60397ffe..77b564cfba 100644 --- a/trytond/trytond/ir/translation.py +++ b/trytond/trytond/ir/translation.py @@ -15,8 +15,8 @@ from sql.aggregate import Max from sql.conditionals import Case from sql.functions import Position, Substring +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.exceptions import UserError from trytond.i18n import gettext from trytond.model import Index, ModelSQL, ModelView, fields diff --git a/trytond/trytond/model/dictschema.py b/trytond/trytond/model/dictschema.py index 392dbd603c..b39d916502 100644 --- a/trytond/trytond/model/dictschema.py +++ b/trytond/trytond/model/dictschema.py @@ -3,8 +3,8 @@ import json from collections import OrderedDict +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.i18n import gettext, lazy_gettext from trytond.model import fields from trytond.model.exceptions import ValidationError @@ -14,9 +14,6 @@ from trytond.rpc import RPC from trytond.tools import slugify from trytond.transaction import Transaction -_request_timeout = config.getint('request', 'timeout', default=0) -_request_records_limit = config.getint('request', 'records_limit') - class DomainError(ValidationError): pass @@ -97,13 +94,13 @@ class DictSchemaMixin(object): 'get_keys': RPC( instantiate=0, size_limits={ - 0: _request_records_limit, + 0: config.getint('request', 'records_limit'), }), 'search_get_keys': RPC( size_limits={ - 1: _request_records_limit, + 1: config.getint('request', 'records_limit'), }, - timeout=_request_timeout), + timeout=config.getint('request', 'timeout', default=0)), }) @staticmethod diff --git a/trytond/trytond/model/fields/many2one.py b/trytond/trytond/model/fields/many2one.py index 646c5933c1..aa0d3234aa 100644 --- a/trytond/trytond/model/fields/many2one.py +++ b/trytond/trytond/model/fields/many2one.py @@ -10,7 +10,7 @@ from sql.conditionals import Coalesce from sql.functions import CharLength from sql.operators import Exists, Or -from trytond.config import config +import trytond.config as config from trytond.pool import Pool from trytond.pyson import PYSONEncoder from trytond.tools import cached_property, reduce_ids @@ -20,8 +20,6 @@ from .field import ( Field, context_validate, domain_method, instantiate_context, order_method, search_order_validate) -_subquery_threshold = config.getint('database', 'subquery_threshold') - class Many2One(Field): _type = 'many2one' @@ -225,7 +223,8 @@ class Many2One(Field): pool = Pool() Rule = pool.get('ir.rule') Target = self.get_target() - use_subquery = Target.estimated_count() < _subquery_threshold + subquery_threshold = config.getint('database', 'subquery_threshold') + use_subquery = Target.estimated_count() < subquery_threshold table, _ = tables[None] name, operator, value = domain[:3] diff --git a/trytond/trytond/model/fields/one2many.py b/trytond/trytond/model/fields/one2many.py index 02d55c0c35..1c279da6ac 100644 --- a/trytond/trytond/model/fields/one2many.py +++ b/trytond/trytond/model/fields/one2many.py @@ -8,7 +8,7 @@ from sql import Literal from sql.conditionals import Coalesce from sql.operators import Exists -from trytond.config import config +import trytond.config as config from trytond.pool import Pool from trytond.pyson import PYSONEncoder from trytond.tools import cached_property, grouped_slice @@ -19,8 +19,6 @@ from .field import ( instanciate_values, instantiate_context, search_order_validate, size_validate) -_subquery_threshold = config.getint('database', 'subquery_threshold') - class One2Many(Field): _type = 'one2many' @@ -347,7 +345,8 @@ class One2Many(Field): origin_where = origin.like(Model.__name__ + ',%') origin = origin_field.sql_id(origin, Target) - use_in = Target.estimated_count() < _subquery_threshold + subquery_threshold = config.getint('database', 'subquery_threshold') + use_in = Target.estimated_count() < subquery_threshold if '.' not in name: if value is None: if use_in: diff --git a/trytond/trytond/model/modelsql.py b/trytond/trytond/model/modelsql.py index 4cfa48db07..acca5e62d6 100644 --- a/trytond/trytond/model/modelsql.py +++ b/trytond/trytond/model/modelsql.py @@ -13,9 +13,8 @@ from sql.conditionals import Coalesce from sql.functions import CurrentTimestamp, Extract, RowNumber, Substring from sql.operators import And, Concat, Equal, Exists, Operator, Or -from trytond import backend +from trytond import backend, config from trytond.cache import freeze -from trytond.config import config from trytond.exceptions import ConcurrencyException from trytond.i18n import gettext from trytond.pool import Pool @@ -34,8 +33,6 @@ from .modelstorage import ( ValidationError, is_leaf) from .modelview import ModelView -_request_records_limit = config.getint('request', 'records_limit') - class ForeignKeyError(ValidationError): pass @@ -356,7 +353,7 @@ class ModelSQL(ModelStorage): cls.__rpc__.update({ 'history_revisions': RPC( size_limits={ - 0: _request_records_limit, + 0: config.getint('request', 'records_limit'), }), }) if cls._history: diff --git a/trytond/trytond/model/modelstorage.py b/trytond/trytond/model/modelstorage.py index ba755438c4..dc396db123 100644 --- a/trytond/trytond/model/modelstorage.py +++ b/trytond/trytond/model/modelstorage.py @@ -16,8 +16,8 @@ from io import StringIO from itertools import chain, groupby, islice from operator import itemgetter +import trytond.config as config from trytond.cache import Cache, LRUDictTransaction, freeze, unfreeze -from trytond.config import config from trytond.const import OPERATORS from trytond.exceptions import UserError from trytond.i18n import gettext, lazy_gettext @@ -36,13 +36,6 @@ from .descriptors import dualmethod from .model import Model __all__ = ['ModelStorage', 'EvalEnvironment'] -_cache_field = config.getint('cache', 'field') -_cache_count_timeout = config.getint( - 'cache', 'count_timeout', default=60 * 60 * 24) -_cache_count_clear = config.getint( - 'cache', 'count_clear', default=1000) -_request_timeout = config.getint('request', 'timeout', default=0) -_request_records_limit = config.getint('request', 'records_limit') def local_cache(Model, transaction=None): @@ -125,7 +118,10 @@ class ModelStorage(Model): xml_id = fields.Function( fields.Char(lazy_gettext('ir.msg_xml_id')), 'get_xml_id') _count_cache = Cache( - 'modelstorage.count', duration=_cache_count_timeout, context=False) + 'modelstorage.count', + duration=config.getint( + 'cache', 'count_timeout', default=60 * 60 * 24), + context=False) _log = None @classmethod @@ -133,74 +129,76 @@ class ModelStorage(Model): from .modelview import ModelView super().__setup__() if issubclass(cls, ModelView): + request_timeout = config.getint('request', 'timeout', default=0) + request_records_limit = config.getint('request', 'records_limit') cls.__rpc__.update({ 'create': RPC( readonly=False, size_limits={ - 0: _request_records_limit, + 0: request_records_limit, }, result=lambda r: list(map(int, r))), 'read': RPC( - timeout=_request_timeout, + timeout=request_timeout, size_limits={ - 0: _request_records_limit, + 0: request_records_limit, }), 'write': RPC( readonly=False, instantiate=slice(0, None, 2), size_limits={ - (0, None, 2): _request_records_limit, + (0, None, 2): request_records_limit, }), 'delete': RPC( readonly=False, instantiate=0, size_limits={ - (0, None, 2): _request_records_limit, + (0, None, 2): request_records_limit, }), 'copy': RPC( readonly=False, instantiate=0, unique=False, size_limits={ - 0: _request_records_limit, + 0: request_records_limit, }, result=lambda r: list(map(int, r))), 'search': RPC( result=lambda r: list(map(int, r)), size_limits={ - 2: _request_records_limit, + 2: request_records_limit, }, - timeout=_request_timeout), + timeout=request_timeout), 'search_count': RPC( - timeout=_request_timeout, + timeout=request_timeout, size_limits={ - 2: _request_records_limit, + 2: request_records_limit, }), 'search_read': RPC( - timeout=_request_timeout, + timeout=request_timeout, size_limits={ - 2: _request_records_limit, + 2: request_records_limit, }), 'resources': RPC( instantiate=0, unique=False, - timeout=_request_timeout), + timeout=request_timeout), 'export_data_domain': RPC( - timeout=_request_timeout, + timeout=request_timeout, size_limits={ - 4: _request_records_limit, + 4: request_records_limit, }), 'export_data': RPC( instantiate=0, unique=False, size_limits={ - 0: _request_records_limit, + 0: request_records_limit, }, - timeout=_request_timeout), + timeout=request_timeout), 'import_data': RPC( readonly=False, size_limits={ - 1: _request_records_limit, + 1: request_records_limit, }), }) if cls._log is None: @@ -267,7 +265,8 @@ class ModelStorage(Model): count = cls._count_cache.get(cls.__name__) if count is not None: - if random.random() < 1 / _cache_count_clear: + if random.random() < 1 / config.getint( + 'cache', 'count_clear', default=1000): cls._count_cache.set(cls.__name__, None) else: cls._count_cache.set(cls.__name__, count + len(vlist)) @@ -464,7 +463,8 @@ class ModelStorage(Model): count = cls._count_cache.get(cls.__name__) if count is not None: - if random.random() < 1 / _cache_count_clear: + if random.random() < 1 / config.getint( + 'cache', 'count_clear', default=1000): cls._count_cache.set(cls.__name__, None) else: cls._count_cache.set(cls.__name__, count - len(records)) @@ -1889,7 +1889,7 @@ class ModelStorage(Model): ifields = filter(to_load, filter(not_cached, iter(self._fields.items()))) - ifields = islice(ifields, 0, _cache_field) + ifields = islice(ifields, 0, config.getint('cache', 'field')) ffields.update(ifields) # add datetime_field diff --git a/trytond/trytond/model/modelview.py b/trytond/trytond/model/modelview.py index a383ec4d09..ee49fe0673 100644 --- a/trytond/trytond/model/modelview.py +++ b/trytond/trytond/model/modelview.py @@ -5,8 +5,8 @@ from functools import wraps from lxml import etree +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.exceptions import UserError from trytond.i18n import gettext from trytond.pool import Pool @@ -21,7 +21,6 @@ from .fields.field import _set_value from .model import Model __all__ = ['ModelView'] -_request_records_limit = config.getint('request', 'records_limit') class AccessButtonError(UserError): @@ -102,7 +101,7 @@ class ModelView(Model): instantiate=0, result=on_change_result), 'autocomplete': RPC( size_limits={ - 2: _request_records_limit, + 2: config.getint('request', 'records_limit'), }), }) cls._buttons = {} diff --git a/trytond/trytond/modules/__init__.py b/trytond/trytond/modules/__init__.py index 1e9ff0b88e..60306cd38b 100644 --- a/trytond/trytond/modules/__init__.py +++ b/trytond/trytond/modules/__init__.py @@ -13,10 +13,7 @@ from glob import iglob from sql import Table from sql.functions import CurrentTimestamp -import trytond.convert as convert -import trytond.tools as tools -from trytond import __series__ -from trytond.config import config +from trytond import __series__, config, convert, tools from trytond.const import MODULES_GROUP from trytond.exceptions import MissingDependenciesException from trytond.transaction import Transaction diff --git a/trytond/trytond/protocols/dispatcher.py b/trytond/trytond/protocols/dispatcher.py index 4d97bf5897..70290ed5bd 100644 --- a/trytond/trytond/protocols/dispatcher.py +++ b/trytond/trytond/protocols/dispatcher.py @@ -7,8 +7,7 @@ import time from sql import Table -from trytond import __series__, backend, security -from trytond.config import config, get_hostname +from trytond import __series__, backend, config, security from trytond.exceptions import ( ConcurrencyException, LoginException, RateLimitException, UserError, UserWarning) @@ -122,7 +121,7 @@ def db_list(request, *args): if not config.getboolean('database', 'list'): abort(HTTPStatus.FORBIDDEN) context = {'_request': request.context} - hostname = get_hostname(request.host) + hostname = config.get_hostname(request.host) with Transaction().start( None, 0, context=context, readonly=True, close=True, ) as transaction: diff --git a/trytond/trytond/protocols/wrappers.py b/trytond/trytond/protocols/wrappers.py index 9b3cd99e03..a5c90edfea 100644 --- a/trytond/trytond/protocols/wrappers.py +++ b/trytond/trytond/protocols/wrappers.py @@ -18,8 +18,7 @@ from werkzeug.utils import redirect, send_file from werkzeug.wrappers import Request as _Request from werkzeug.wrappers import Response -from trytond import backend, security -from trytond.config import config, get_hostname +from trytond import backend, config, security from trytond.exceptions import RateLimitException, UserError, UserWarning from trytond.pool import Pool from trytond.tools import cached_property @@ -193,7 +192,7 @@ def with_pool(func): database_list = Pool.database_list() if database_name not in database_list: with Transaction().start(None, 0, readonly=True) as transaction: - hostname = get_hostname(request.host) + hostname = config.get_hostname(request.host) db_list = transaction.database.list(hostname=hostname) if database_name not in db_list: abort(HTTPStatus.NOT_FOUND) diff --git a/trytond/trytond/report/report.py b/trytond/trytond/report/report.py index dd461c98fd..136aa76ae0 100644 --- a/trytond/trytond/report/report.py +++ b/trytond/trytond/report/report.py @@ -35,7 +35,7 @@ except ImportError: from genshi.filters import Translator from genshi.template.text import TextTemplate -from trytond.config import config +import trytond.config as config from trytond.i18n import gettext, ngettext from trytond.model.exceptions import AccessError from trytond.pool import Pool, PoolBase @@ -115,13 +115,6 @@ NO_BREAKING_SPACE = '\u00A0' # round it to 100. REPORT_NAME_MAX_LENGTH = 100 -CONVERT_COMMAND = config.get( - 'report', 'convert_command', - default='soffice --headless --nolockcheck --nodefault --norestore ' - '--convert-to "%(output_extension)s" ' - '--outdir "%(directory)s" ' - '"%(input_path)s"') - class TranslateFactory: @@ -434,7 +427,14 @@ class Report(URLMixin, PoolBase): with open(input_path, mode) as fp: fp.write(data) try: - cmd = CONVERT_COMMAND % { + cmd = config.get( + 'report', 'convert_command', + default='soffice --headless ' + '--nolockcheck --nodefault --norestore ' + '--convert-to "%(output_extension)s" ' + '--outdir "%(directory)s" ' + '"%(input_path)s"') + cmd %= { 'directory': directory, 'input_format': input_format, 'input_extension': input_extension, diff --git a/trytond/trytond/res/routes.py b/trytond/trytond/res/routes.py index a9064c042b..00410c5db0 100644 --- a/trytond/trytond/res/routes.py +++ b/trytond/trytond/res/routes.py @@ -4,7 +4,7 @@ import logging import random import time -from trytond.config import config +import trytond.config as config from trytond.protocols.wrappers import ( abort, allow_null_origin, with_pool, with_transaction) from trytond.transaction import Transaction diff --git a/trytond/trytond/res/user.py b/trytond/trytond/res/user.py index ba153b1ae9..fc35b6a70d 100644 --- a/trytond/trytond/res/user.py +++ b/trytond/trytond/res/user.py @@ -30,8 +30,8 @@ from sql.aggregate import Count from sql.conditionals import Case from sql.functions import CurrentTimestamp +import trytond.config as config from trytond.cache import Cache -from trytond.config import config from trytond.exceptions import LoginException, RateLimitException, UserError from trytond.i18n import gettext, ngettext from trytond.model import ( diff --git a/trytond/trytond/security.py b/trytond/trytond/security.py index 6fe0eea9f0..1e239199f9 100644 --- a/trytond/trytond/security.py +++ b/trytond/trytond/security.py @@ -16,8 +16,7 @@ from sql import Table from sql.conditionals import Coalesce from werkzeug.exceptions import abort -from trytond import backend -from trytond.config import config +from trytond import backend, config from trytond.exceptions import LoginException, RateLimitException from trytond.pool import Pool from trytond.transaction import Transaction diff --git a/trytond/trytond/sendmail.py b/trytond/trytond/sendmail.py index b0f416db54..46fe7c1f90 100644 --- a/trytond/trytond/sendmail.py +++ b/trytond/trytond/sendmail.py @@ -8,7 +8,7 @@ from email.message import EmailMessage, Message from email.utils import formatdate, make_msgid from urllib.parse import parse_qs, unquote_plus -from trytond.config import config, parse_uri +import trytond.config as config from trytond.transaction import Transaction from trytond.url import host @@ -17,7 +17,6 @@ __all__ = [ 'send_message_transactional', 'send_message', 'SMTPDataManager'] logger = logging.getLogger(__name__) -retry = config.getint('email', 'retry', default=5) def sendmail_transactional( @@ -59,6 +58,7 @@ def send_message( quit = False if 'Date' not in msg: msg['Date'] = formatdate() + retry = config.getint('email', 'retry', default=5) for count in range(retry, -1, -1): if count != retry: time.sleep(0.02 * (retry - count)) @@ -110,7 +110,7 @@ def get_smtp_server(uri=None, strict=False): if uri is None: uri = config.get('email', 'uri') ini_uri = uri - uri = parse_uri(uri) + uri = config.parse_uri(uri) extra = {} if uri.query: cast = {'timeout': int} diff --git a/trytond/trytond/status.py b/trytond/trytond/status.py index 690b8b5a9d..4b243208f3 100644 --- a/trytond/trytond/status.py +++ b/trytond/trytond/status.py @@ -10,7 +10,7 @@ import time from collections import namedtuple from contextlib import contextmanager -from trytond.config import config +import trytond.config as config status = dict() logger = logging.getLogger(__name__) diff --git a/trytond/trytond/tests/test_bus.py b/trytond/trytond/tests/test_bus.py index c7a7d12347..171a4208f1 100644 --- a/trytond/trytond/tests/test_bus.py +++ b/trytond/trytond/tests/test_bus.py @@ -5,7 +5,7 @@ import time import unittest from unittest.mock import patch -from trytond import backend, bus +from trytond import backend, config from trytond.bus import Bus, _MessageQueue, notify from trytond.tests.test_tryton import ( DB_NAME, TestCase, activate_module, with_transaction) @@ -85,15 +85,15 @@ class BusTestCase(TestCase): def setUp(self): super().setUp() - reset_polling_timeout = bus._long_polling_timeout - bus._long_polling_timeout = 1 + polling_timeout = config.get('bus', 'long_polling_timeout') + config.set('bus', 'long_polling_timeout', '1') self.addCleanup( - setattr, bus, '_long_polling_timeout', reset_polling_timeout) + config.set, 'bus', 'long_polling_timeout', polling_timeout) - reset_select_timeout = bus._select_timeout - bus._select_timeout = 1 + select_timeout = config.get('bus', 'select_timeout') + config.set('bus', 'select_timeout', '1') self.addCleanup( - setattr, bus, '_select_timeout', reset_select_timeout) + config.set, 'bus', 'select_timeout', select_timeout) def tearDown(self): pid = os.getpid() diff --git a/trytond/trytond/tests/test_cache.py b/trytond/trytond/tests/test_cache.py index 552d694d22..d2356ecc9b 100644 --- a/trytond/trytond/tests/test_cache.py +++ b/trytond/trytond/tests/test_cache.py @@ -6,8 +6,7 @@ import time import unittest from unittest.mock import patch -from trytond import backend -from trytond import cache as cache_mod +from trytond import backend, config from trytond.cache import ( LRUDict, LRUDictTransaction, MemoryCache, freeze, unfreeze) from trytond.tests.test_tryton import ( @@ -106,10 +105,10 @@ class MemoryCacheTestCase(TestCase): def setUp(self): super().setUp() - clear_timeout = cache_mod._clean_timeout - cache_mod._clean_timeout = 1 + clean_timeout = config.get('cache', 'clean_timeout') + config.set('cache', 'clean_timeout', '1') self.addCleanup( - setattr, cache_mod, '_clean_timeout', clear_timeout) + config.set, 'cache', 'clean_timeout', clean_timeout) def tearDown(self): MemoryCache.drop(DB_NAME) @@ -176,7 +175,7 @@ class MemoryCacheTestCase(TestCase): with Transaction().start(DB_NAME, USER): cache.clear() # Ensure sync is performed on start - time.sleep(cache_mod._clean_timeout) + time.sleep(config.getint('cache', 'clean_timeout')) with Transaction().start(DB_NAME, USER) as transaction1: cache.clear() @@ -187,7 +186,7 @@ class MemoryCacheTestCase(TestCase): "Test MemoryCache synchronisation" with Transaction().start(DB_NAME, USER): cache.clear() - time.sleep(cache_mod._clean_timeout) + time.sleep(config.getint('cache', 'clean_timeout')) last = cache._clean_last with Transaction().start(DB_NAME, USER): @@ -243,10 +242,10 @@ class MemoryCacheChannelTestCase(MemoryCacheTestCase): def setUp(self): super().setUp() - clear_timeout = cache_mod._clean_timeout - cache_mod._clean_timeout = 0 + clean_timeout = config.get('cache', 'clean_timeout') + config.set('cache', 'clean_timeout', '0') self.addCleanup( - setattr, cache_mod, '_clean_timeout', clear_timeout) + config.set, 'cache', 'clean_timeout', clean_timeout) def wait_cache_sync(self, after=None): if after is None: diff --git a/trytond/trytond/tests/test_field_binary.py b/trytond/trytond/tests/test_field_binary.py index 3b0a8b6e2c..7dc03ea0cc 100644 --- a/trytond/trytond/tests/test_field_binary.py +++ b/trytond/trytond/tests/test_field_binary.py @@ -5,7 +5,7 @@ import tempfile from sql import Literal -from trytond import filestore +from trytond import config from trytond.model import fields from trytond.model.exceptions import ( RequiredValidationError, SQLConstraintError) @@ -27,10 +27,10 @@ class FieldBinaryTestCase(TestCase): def setUp(self): super().setUp() - path = filestore.PATH + path = config.get('database', 'path') dtemp = tempfile.mkdtemp() - filestore.PATH = dtemp - self.addCleanup(setattr, filestore, 'PATH', path) + config.set('database', 'path', dtemp) + self.addCleanup(config.set, 'database', 'path', path) self.addCleanup(shutil.rmtree, dtemp) @with_transaction() diff --git a/trytond/trytond/tests/test_field_many2one.py b/trytond/trytond/tests/test_field_many2one.py index ac723cb09c..6e5e27e94d 100644 --- a/trytond/trytond/tests/test_field_many2one.py +++ b/trytond/trytond/tests/test_field_many2one.py @@ -5,7 +5,7 @@ from unittest.mock import patch from sql import Join, Table -from trytond.config import config +import trytond.config as config from trytond.model.exceptions import DomainValidationError from trytond.pool import Pool from trytond.tests.test_tryton import ( @@ -354,12 +354,10 @@ class FieldMany2OneExistsTestCase( activate_module('tests') def setUp(self): - from trytond.model.fields import many2one super().setUp() - previous = int( - config.get('database', 'subquery_threshold', default='1_000')) - many2one._subquery_threshold = 0 - self.addCleanup(setattr, many2one, '_subquery_threshold', previous) + previous = config.get('database', 'subquery_threshold') + config.set('database', 'subquery_threshold', '0') + self.addCleanup(config.set, 'database', 'subquery_threshold', previous) class FieldMany2OneTreeTestCase(TestCase): diff --git a/trytond/trytond/tests/test_field_one2many.py b/trytond/trytond/tests/test_field_one2many.py index 8cb118c901..59804b3873 100644 --- a/trytond/trytond/tests/test_field_one2many.py +++ b/trytond/trytond/tests/test_field_one2many.py @@ -1,7 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.config import config +import trytond.config as config from trytond.model.exceptions import ( DomainValidationError, RequiredValidationError, SizeValidationError) from trytond.pool import Pool @@ -746,12 +746,10 @@ class FieldOne2ManyExistsTestCase(TestCase, SearchTestCaseMixin): activate_module('tests') def setUp(self): - from trytond.model.fields import one2many super().setUp() - previous = int( - config.get('database', 'subquery_threshold', default='1_000')) - one2many._subquery_threshold = 0 - self.addCleanup(setattr, one2many, '_subquery_threshold', previous) + previous = config.get('database', 'subquery_threshold') + config.set('database', 'subquery_threshold', '0') + self.addCleanup(config.set, 'database', 'subquery_threshold', previous) def One2Many(self): return Pool().get('test.one2many') @@ -771,12 +769,10 @@ class FieldOne2ManyReferenceExistsTestCase( activate_module('tests') def setUp(self): - from trytond.model.fields import one2many super().setUp() - previous = int( - config.get('database', 'subquery_threshold', default='1_000')) - one2many._subquery_threshold = 0 - self.addCleanup(setattr, one2many, '_subquery_threshold', previous) + previous = config.get('database', 'subquery_threshold') + config.set('database', 'subquery_threshold', '0') + self.addCleanup(config.set, 'database', 'subquery_threshold', previous) def One2Many(self): return Pool().get('test.one2many_reference') diff --git a/trytond/trytond/tests/test_filestore.py b/trytond/trytond/tests/test_filestore.py index 382b6134bf..342cc771fc 100644 --- a/trytond/trytond/tests/test_filestore.py +++ b/trytond/trytond/tests/test_filestore.py @@ -5,7 +5,7 @@ import shutil import tempfile from unittest.mock import patch -from trytond.config import config +import trytond.config as config from trytond.filestore import filestore from trytond.tests.test_tryton import TestCase @@ -17,10 +17,7 @@ class FileStoreTestCase(TestCase): path = config.get('database', 'path') dtemp = tempfile.mkdtemp() config.set('database', 'path', dtemp) - patcher = patch('trytond.filestore.PATH', dtemp) - patcher.start() self.addCleanup(config.set, 'database', 'path', path) - self.addCleanup(patcher.stop) self.addCleanup(shutil.rmtree, dtemp) def data(self): diff --git a/trytond/trytond/tests/test_tryton.py b/trytond/trytond/tests/test_tryton.py index b280527594..63ba0d0fa0 100644 --- a/trytond/trytond/tests/test_tryton.py +++ b/trytond/trytond/tests/test_tryton.py @@ -27,9 +27,8 @@ from lxml import etree from sql import Table from werkzeug.test import Client -from trytond import backend +from trytond import backend, config from trytond.cache import Cache -from trytond.config import config, parse_uri from trytond.model import ( ModelSingleton, ModelSQL, ModelStorage, ModelView, Workflow, fields) from trytond.model.fields import Function @@ -188,7 +187,7 @@ def _sqlite_copy(file_, restore=False): def _pg_options(): - uri = parse_uri(config.get('database', 'uri')) + uri = config.parse_uri(config.get('database', 'uri')) options = [] env = os.environ.copy() if uri.hostname: diff --git a/trytond/trytond/tests/test_user.py b/trytond/trytond/tests/test_user.py index 4856bcc4c3..e888738cde 100644 --- a/trytond/trytond/tests/test_user.py +++ b/trytond/trytond/tests/test_user.py @@ -5,7 +5,7 @@ import os from contextlib import contextmanager from unittest.mock import Mock, patch -from trytond.config import config +import trytond.config as config from trytond.pool import Pool from trytond.res import user as user_module from trytond.res.user import PasswordError @@ -49,7 +49,7 @@ class UserTestCase(TestCase): os.path.join(os.path.dirname(__file__), 'forbidden.txt')) self.addCleanup(config.set, 'password', 'forbidden', forbidden) - reset_from = config.get('email', 'from', fallback='') + reset_from = config.get('email', 'from', default='') config.set('email', 'from', FROM) self.addCleanup(lambda: config.set('email', 'from', reset_from)) diff --git a/trytond/trytond/transaction.py b/trytond/trytond/transaction.py index 30d509e861..bf73401ec0 100644 --- a/trytond/trytond/transaction.py +++ b/trytond/trytond/transaction.py @@ -12,17 +12,13 @@ from weakref import WeakValueDictionary from sql import Flavor -from trytond.config import config +import trytond.config as config from trytond.tools.immutabledict import ImmutableDict __all__ = ['Transaction', 'check_access', 'without_check_access', 'active_records', 'inactive_records'] -_retry = config.getint('database', 'retry') -_cache_transaction = config.getint('cache', 'transaction') -_cache_model = config.getint('cache', 'model') -_cache_record = config.getint('cache', 'record') logger = logging.getLogger(__name__) @@ -54,7 +50,8 @@ class _TransactionLockRecordsError(TransactionError): def record_cache_size(transaction): - return transaction.context.get('_record_cache_size', _cache_record) + return transaction.context.get( + '_record_cache_size', config.getint('cache', 'record')) def check_access(func=None, *, _access=True): @@ -138,7 +135,8 @@ class Transaction(object): instance.timestamp = None instance.started_at = None instance.cache = WeakValueDictionary() - instance._cache_deque = deque(maxlen=_cache_transaction) + instance._cache_deque = deque( + maxlen=config.getint('cache', 'transaction')) instance._atexit = [] transactions.append(instance) else: @@ -162,9 +160,10 @@ class Transaction(object): keys = tuple(((key, self.context[key]) for key in sorted(self.cache_keys) if key in self.context)) + cache_model = config.getint('cache', 'model') cache = self.cache.setdefault( (self.user, keys), LRUDict( - _cache_model, + cache_model, lambda name: LRUDict( record_cache_size(self), Pool().get(name)._record), @@ -210,9 +209,10 @@ class Transaction(object): self.connection = database.get_connection(readonly=readonly, autocommit=autocommit, statement_timeout=timeout) count = 0 + retry = config.getint('database', 'retry') while True: if count: - time.sleep(0.02 * (_retry - count)) + time.sleep(0.02 * (retry - count)) try: lock_tables = extras.get('_lock_tables', []) for table in lock_tables: @@ -224,7 +224,7 @@ class Transaction(object): locked_records[table].update(ids) self._locked_records = locked_records except backend.DatabaseOperationalError: - if count < _retry: + if count < retry: self.connection.rollback() count += 1 logger.debug("Retry: %i", count) diff --git a/trytond/trytond/url.py b/trytond/trytond/url.py index f05ef397cd..b4e86ac1b0 100644 --- a/trytond/trytond/url.py +++ b/trytond/trytond/url.py @@ -5,7 +5,7 @@ import encodings.idna import socket import urllib.parse -from trytond.config import config +import trytond.config as config from trytond.transaction import Transaction __all__ = ['URLMixin', 'is_secure', 'host', 'http_host'] diff --git a/trytond/trytond/worker.py b/trytond/trytond/worker.py index 57322fed86..942a759fab 100644 --- a/trytond/trytond/worker.py +++ b/trytond/trytond/worker.py @@ -12,8 +12,7 @@ from multiprocessing import cpu_count from sql import Flavor -from trytond import backend -from trytond.config import config +from trytond import backend, config from trytond.exceptions import UserError, UserWarning from trytond.pool import Pool from trytond.status import processing diff --git a/trytond/trytond/wsgi.py b/trytond/trytond/wsgi.py index 57ef856320..237bf70175 100644 --- a/trytond/trytond/wsgi.py +++ b/trytond/trytond/wsgi.py @@ -27,8 +27,7 @@ try: except ImportError: from werkzeug.wsgi import SharedDataMiddleware -from trytond import backend, security -from trytond.config import config +from trytond import backend, config, security from trytond.protocols.jsonrpc import JSONProtocol from trytond.protocols.wrappers import ( HTTPStatus, Request, Response, abort, exceptions)