Refactor of trytond.config to add cache

This removes the need to store configuration in global variable and so make it
easier to update for example for testing.

Closes #14249
This commit is contained in:
Cédric Krier
2025-10-07 08:19:57 +02:00
parent 5fdcba985b
commit 74ee169c54
108 changed files with 505 additions and 405 deletions
+1 -1
View File
@@ -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
+1 -2
View File
@@ -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
+9 -8
View File
@@ -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':
+1 -1
View File
@@ -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
+10 -9
View File
@@ -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,
+5 -4
View File
@@ -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()
+1 -2
View File
@@ -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,
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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,
+5 -6
View File
@@ -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)
@@ -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
+1 -1
View File
@@ -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)
+2 -1
View File
@@ -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
+4 -3
View File
@@ -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)
+3 -3
View File
@@ -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,
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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]
@@ -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
@@ -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
+5 -5
View File
@@ -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
+5 -7
View File
@@ -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):
+1 -2
View File
@@ -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
+1 -1
View File
@@ -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
@@ -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
+4 -3
View File
@@ -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
+2 -1
View File
@@ -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
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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
+6 -4
View File
@@ -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):
+3 -4
View File
@@ -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/<code>/<base64:database_name>',
@@ -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
+3 -5
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
@@ -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)
+4 -4
View File
@@ -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):
@@ -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
+4 -4
View File
@@ -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
+1 -1
View File
@@ -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
+8 -3
View File
@@ -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:
+1 -1
View File
@@ -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)
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 (
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1
View File
@@ -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
+2 -2
View File
@@ -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'):
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+81
View File
@@ -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`.
+1
View File
@@ -9,6 +9,7 @@ Reference of the ``trytond`` API:
.. toctree::
:maxdepth: 1
config
models
fields
wizard
+1 -2
View File
@@ -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
+1 -1
View File
@@ -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',
+10 -9
View File
@@ -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()
+2 -3
View File
@@ -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")
+11 -15
View File
@@ -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('/<string:database_name>/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', [])
+11 -11
View File
@@ -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()
+1 -1
View File
@@ -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(
+63 -36
View File
@@ -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}")
+13 -4
View File
@@ -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
+2 -1
View File
@@ -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
+3 -3
View File
@@ -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,
+2 -1
View File
@@ -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
+2 -3
View File
@@ -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)])
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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)])
+3 -3
View File
@@ -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
+6 -8
View File
@@ -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:
+12 -14
View File
@@ -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 = '''<!DOCTYPE html>
@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
+6 -7
View File
@@ -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',
+1 -1
View File
@@ -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
+4 -7
View File
@@ -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
+3 -4
View File
@@ -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]
+3 -4
View File
@@ -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:
+2 -5
View File
@@ -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:
+30 -30
View File
@@ -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
+2 -3
View File
@@ -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 = {}
+1 -4
View File
@@ -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
+2 -3
View File
@@ -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:
+2 -3
View File
@@ -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)
+9 -9
View File
@@ -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,
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 (
+1 -2
View File
@@ -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
+3 -3
View File
@@ -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}
+1 -1
View File
@@ -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__)
+7 -7
View File
@@ -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()
+9 -10
View File
@@ -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:
+4 -4
View File
@@ -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()
+4 -6
View File
@@ -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):

Some files were not shown because too many files have changed in this diff Show More