Refactore configuration to allow easy extention

issue3627
review7401002
This commit is contained in:
Cédric Krier
2014-09-03 23:32:01 +02:00
parent 6765a12e5a
commit e0b995abbe
48 changed files with 613 additions and 621 deletions
+1
View File
@@ -1,3 +1,4 @@
* Refactor configuration file and command line
* Use the context of the relation field for instanciation
* Use a configuration field for logging
* Add translated descriptor for Selection field
-1
View File
@@ -8,7 +8,6 @@ include doc/*
recursive-include doc *.rst
recursive-include doc *.po
recursive-include doc *.pot
include etc/*
include trytond/backend/*/init.sql
include trytond/ir/tryton.cfg
include trytond/ir/*.xml
+15 -48
View File
@@ -21,72 +21,39 @@ def parse_commandline():
parser.add_argument('--version', action='version',
version='%(prog)s ' + VERSION)
parser.add_argument("-c", "--config", dest="config",
parser.add_argument("-c", "--config", dest="configfile",
help="specify config file")
parser.add_argument('--debug', dest='debug_mode', action='store_true',
help='enable debug mode (start post-mortem debugger if exceptions'
' occur)')
parser.add_argument('--debug', dest='debug', action='store_true',
help='enable debug mode')
parser.add_argument("-v", "--verbose", action="store_true",
dest="verbose", help="enable verbose mode")
parser.add_argument("-d", "--database", dest="db_name",
help="specify the database name")
parser.add_argument("-i", "--init", dest="init",
parser.add_argument("-d", "--database", dest="database_names", nargs='+',
default=[], help="specify the database name")
parser.add_argument("-i", "--init", dest="init", nargs='+', default=[],
help="init a module (use \"all\" for all modules)")
parser.add_argument("-u", "--update", dest="update",
parser.add_argument("-u", "--update", dest="update", nargs='+', default=[],
help="update a module (use \"all\" for all modules)")
parser.add_argument("--pidfile", dest="pidfile",
help="file where the server pid will be stored")
parser.add_argument("--logconf", dest="logconf",
help="logging configuration file (ConfigParser format)")
parser.add_argument("--disable-cron", dest="cron",
action="store_false", help="disable cron")
parser.add_argument("--cron", dest="cron", action="store_true",
help="enable cron")
parser.epilog = ('The first time a database is initialized with "-i" admin'
' password is read from file defined by TRYTONPASSFILE '
'environment variable or interactively ask user. '
'environment variable or interactively ask user.\n'
'The config file can be specified in the TRYTOND_CONFIG '
'environment variable.\n'
'The database URI can be specified in the TRYTOND_DATABASE_URI '
'environment variable.')
opt = parser.parse_args()
if opt.config:
options['configfile'] = opt.config
else:
# No config file speficified, it will be guessed
options['configfile'] = None
for arg in (
'verbose',
'debug_mode',
'pidfile',
'logconf',
'cron',
):
if getattr(opt, arg) is not None:
options[arg] = getattr(opt, arg)
db_name = []
if opt.db_name:
for i in opt.db_name.split(','):
db_name.append(i)
options['db_name'] = db_name
init = {}
if opt.init:
for i in opt.init.split(','):
if i != 'test':
init[i] = 1
options['init'] = init
update = {}
if opt.update:
for i in opt.update.split(','):
if i != 'test':
update[i] = 1
options['update'] = update
options = parser.parse_args()
if not options.database_names and bool(options.init or options.update):
parser.error('Missing database option')
return options
+1
View File
@@ -25,6 +25,7 @@ First steps
* **Installation:**
:ref:`Installation <topics-install>`
:ref:`Configuration <topics-configuration>`
The model layer
===============
+193
View File
@@ -0,0 +1,193 @@
.. _topics-configuration:
=============================
Configuration file for Tryton
=============================
The configuration file control some aspects of the behavior of Tryton.
The file uses a simple ini-file format. It consists of sections, led by a
`[section]` header and followed by `name = value` entries:
[database]
uri = postgresql://user:password@localhost/
path = /var/lib/trytond
For more information see ConfigParser_.
.. _ConfigParser: http://docs.python.org/2/library/configparser.html
Sections
========
This section describes the different main sections that may appear in a Tryton
configuration file, the purpose of each section, its possible keys, and their
possible values.
Some modules could request the usage of other sections for which the guideline
asks them to be named like their module.
jsonrpc
-------
Defines the behavior of the JSON-RPC_ network interface.
listen
~~~~~~
Defines a comma separated list of couple of host (or IP address) and port numer
separeted by a colon to listen on.
The default value is `localhost:8000`.
hostname
~~~~~~~~
Defines the hostname for this network interface.
data
~~~~
Defines the root path to retrieve data for `GET` request.
xmlrpc
------
Defines the behavior of the XML-RPC_ network interface.
listen
~~~~~~
Same as for `jsonrpc` except it have no default value.
webdav
------
Define the behavior of the WebDAV_ network interface.
listen
~~~~~~
Same as for `jsonrpc` except it have no default value.
database
--------
Defines how database is managed.
uri
~~~
Contains the URI to connect to the SQL database. The URI follows the RFC-3986_.
The typical form is:
database://username:password@host:port/
The default available databases are:
PostgreSQL
**********
`pyscopg2` supports two type of connections:
- TCP/IP connection: `postgresql://user:password@localhost:5432/`
- Unix domain connection: `postgresql://username:password@/`
SQLite
******
The only possible URI is: `sqlite://`
MySQL
*****
Same as for PostgreSQL.
path
~~~~
The directory where Tryton should store files and so the user running `trytond`
must have write access on this directory.
The default value is `/var/lib/trytond/`.
list
~~~~
A boolean value (default: `True`) to list available databases.
retry
~~~~~
The number of retries when a database operation error occurs during a request.
language
~~~~~~~~
The main language (default: `en_US`) of the database that will be stored in the
main table for translatable fields.
ssl
---
Activates the SSL_ on all network protocol.
privatekey
~~~~~~~~~~
The path to the private key.
certificate
~~~~~~~~~~~
The path to the certificate.
email
-----
uri
~~~
The SMTP-URL_ to connect to the SMTP server which is extended to support SSL_
and STARTTLS_.
The available protocols are:
- `smtp`: simple SMTP
- `smtp+tls`: SMTP with STARTTLS
- `smtps`: SMTP with SSL
The default value is: `smtp://localhost:25`
from
~~~~
Defines the default `From` address when Tryton send emails.
session
-------
timeout
~~~~~~~
The time in second before a session expires.
super_pwd
~~~~~~~~~
Theserver password uses to authenticate database management from the client.
It is encrypted using using the Unix `crypt(3)` routine.
Such password can be generated using this command line::
python -c 'import getpass,crypt,random,string; print crypt.crypt(getpass.getpass(), "".join(random.sample(string.ascii_letters + string.digits, 8)))'
report
------
unoconv
~~~~~~~
The parameters for `unoconv`.
.. _JSON-RPC: http://en.wikipedia.org/wiki/JSON-RPC
.. _XML-RPC: http://en.wikipedia.org/wiki/XML-RPC
.. _WebDAV: http://en.wikipedia.org/wiki/WebDAV
.. _RFC-3986: http://tools.ietf.org/html/rfc3986
.. _SMTP-URL: http://tools.ietf.org/html/draft-earhart-url-smtp-00
.. _SSL: http://en.wikipedia.org/wiki/Secure_Sockets_Layer
.. _STARTTLS: http://en.wikipedia.org/wiki/STARTTLS
+1
View File
@@ -10,6 +10,7 @@ Introduction to all the key parts of trytond:
:maxdepth: 1
install
configuration
logs
models/index
models/fields_default_value
-88
View File
@@ -1,88 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
[options]
# Activate the json-rpc protocol
jsonrpc = localhost:8000
#ssl_jsonrpc = False
# This is the hostname used when generating tryton URI
#hostname_jsonrpc =
# Configure the path of json-rpc data
#jsondata_path = /var/www/localhost/tryton
# Activate the xml-rpc protocol
#xmlrpc = *:8069
#ssl_xmlrpc = False
# Activate the webdav protocol
#webdav = *:8080
#ssl_webdav = False
# This is the hostname used when generating WebDAV URI
#hostname_webdav =
# Configure the database type
# allowed values are postgresql, sqlite, mysql
#db_type = postgresql
# Configure the database connection
## Note: Only databases owned by db_user will be displayed in the connection dialog
## of the Tryton client. db_user must have create permission for new databases
## to be able to use automatic database creation with the Tryton client.
#db_host = False
#db_port = False
#db_user = False
#db_password = False
#db_minconn = 1
#db_maxconn = 64
# Configure the postgresql path for the executable
#pg_path = None
# Configure the Tryton server password
#admin_passwd = admin
# Configure the path of the files for the pid and the logs
#pidfile = False
#logconf = False
#privatekey = server.pem
#certificate = server.pem
# Configure the SMTP connection
#smtp_server = localhost
#smtp_port = 25
#smtp_ssl = False
#smtp_tls = False
#smtp_password = False
#smtp_user = False
#smtp_default_from_email = False
# Configure the path to store attachments and sqlite database
#data_path = /var/lib/trytond
# Allow to run more than one instance of trytond
#multi_server = False
# Configure the session timeout (inactivity of the client in sec)
#session_timeout = 600
# Enable auto-reload of modules if changed
#auto_reload = True
# Prevent database listing
#prevent_dblist = False
# Enable cron
# cron = True
# unoconv connection
#unoconv = pipe,name=trytond;urp;StarOffice.ComponentContext
# Number of retries on database operational error
# retry = 5
# Default language code
# language = en_US
+10 -5
View File
@@ -1,15 +1,20 @@
#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 sys
import urlparse
from trytond.config import CONFIG
from trytond.config import config
__all__ = ['get']
__all__ = ['name', 'get']
def get(name):
db_type = CONFIG['db_type']
def name():
return urlparse.urlparse(config.get('database', 'uri', '')).scheme
def get(prop):
db_type = name()
modname = 'trytond.backend.%s' % db_type
__import__(modname)
module = sys.modules[modname]
return getattr(module, name)
return getattr(module, prop)
+34 -30
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.backend.database import DatabaseInterface, CursorInterface
from trytond.config import CONFIG
from trytond.config import config, parse_uri
import MySQLdb
import MySQLdb.cursors
import MySQLdb.converters
@@ -91,14 +91,16 @@ class Database(DatabaseInterface):
'charset': 'utf8',
'conv': conv,
}
if CONFIG['db_host']:
args['host'] = CONFIG['db_host']
if CONFIG['db_port']:
args['port'] = int(CONFIG['db_port'])
if CONFIG['db_user']:
args['user'] = CONFIG['db_user']
if CONFIG['db_password']:
args['passwd'] = CONFIG['db_password']
uri = parse_uri(config.get('database', 'uri'))
assert uri.scheme == 'mysql'
if uri.hostname:
args['host'] = uri.hostname
if uri.port:
args['port'] = uri.port
if uri.username:
args['user'] = uri.username
if uri.password:
args['passwd'] = uri.password
conn = MySQLdb.connect(**args)
cursor = Cursor(conn, self.database_name)
cursor.execute('SET time_zone = `UTC`')
@@ -120,20 +122,21 @@ class Database(DatabaseInterface):
@staticmethod
def dump(database_name):
from trytond.tools import exec_pg_command_pipe
from trytond.tools import exec_command_pipe
cmd = ['mysqldump', '--no-create-db']
if CONFIG['db_user']:
cmd.append('--user=' + CONFIG['db_user'])
if CONFIG['db_host']:
cmd.append('--host=' + CONFIG['db_host'])
if CONFIG['db_port']:
cmd.append('--port=' + CONFIG['db_port'])
if CONFIG['db_password']:
cmd.append('--password=' + CONFIG['db_password'])
uri = parse_uri(config.get('database', 'uri'))
if uri.username:
cmd.append('--user=' + uri.username)
if uri.hostname:
cmd.append('--host=' + uri.hostname)
if uri.port:
cmd.append('--port=' + uri.port)
if uri.password:
cmd.append('--password=' + uri.password)
cmd.append(database_name)
pipe = exec_pg_command_pipe(*tuple(cmd))
pipe = exec_command_pipe(*tuple(cmd))
pipe.stdin.close()
data = pipe.stdout.read()
res = pipe.wait()
@@ -143,7 +146,7 @@ class Database(DatabaseInterface):
@staticmethod
def restore(database_name, data):
from trytond.tools import exec_pg_command_pipe
from trytond.tools import exec_command_pipe
database = Database().connect()
cursor = database.cursor(autocommit=True)
@@ -152,14 +155,15 @@ class Database(DatabaseInterface):
cursor.close()
cmd = ['mysql']
if CONFIG['db_user']:
cmd.append('--user=' + CONFIG['db_user'])
if CONFIG['db_host']:
cmd.append('--host=' + CONFIG['db_host'])
if CONFIG['db_port']:
cmd.append('--port=' + CONFIG['db_port'])
if CONFIG['db_password']:
cmd.append('--password=' + CONFIG['db_password'])
uri = parse_uri(config.get('database', 'uri'))
if uri.username:
cmd.append('--user=' + uri.username)
if uri.hostname:
cmd.append('--host=' + uri.hostname)
if uri.port:
cmd.append('--port=' + uri.port)
if uri.password:
cmd.append('--password=' + uri.password)
cmd.append(database_name)
fd, file_name = tempfile.mkstemp()
@@ -171,7 +175,7 @@ class Database(DatabaseInterface):
args2 = tuple(cmd)
pipe = exec_pg_command_pipe(*args2)
pipe = exec_command_pipe(*args2)
pipe.stdin.close()
res = pipe.wait()
os.remove(file_name)
@@ -192,7 +196,7 @@ class Database(DatabaseInterface):
@staticmethod
def list(cursor):
now = time.time()
timeout = int(CONFIG['session_timeout'])
timeout = config.getint('session', 'timeout')
res = Database._list_cache
if res and abs(Database._list_cache_timestamp - now) < timeout:
return res
+39 -36
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.backend.database import DatabaseInterface, CursorInterface
from trytond.config import CONFIG
from trytond.config import config, parse_uri
from psycopg2.pool import ThreadedConnectionPool
from psycopg2.extensions import cursor as PsycopgCursor
from psycopg2.extensions import ISOLATION_LEVEL_REPEATABLE_READ
@@ -54,14 +54,15 @@ class Database(DatabaseInterface):
return self
logger = logging.getLogger('database')
logger.info('connect to "%s"' % self.database_name)
host = CONFIG['db_host'] and "host=%s" % CONFIG['db_host'] or ''
port = CONFIG['db_port'] and "port=%s" % CONFIG['db_port'] or ''
uri = parse_uri(config.get('database', 'uri'))
assert uri.scheme == 'postgresql'
host = uri.hostname and "host=%s" % uri.hostname or ''
port = uri.port and "port=%s" % uri.port or ''
name = "dbname=%s" % self.database_name
user = CONFIG['db_user'] and "user=%s" % CONFIG['db_user'] or ''
password = (CONFIG['db_password']
and "password=%s" % CONFIG['db_password'] or '')
minconn = int(CONFIG['db_minconn']) or 1
maxconn = int(CONFIG['db_maxconn']) or 64
user = uri.username and "user=%s" % uri.username or ''
password = uri.password and "password=%s" % uri.password or ''
minconn = config.getint('database', 'minconn', 1)
maxconn = config.getint('database', 'maxconn', 64)
dsn = '%s %s %s %s %s' % (host, port, name, user, password)
self._connpool = ThreadedConnectionPool(minconn, maxconn, dsn)
return self
@@ -106,18 +107,25 @@ class Database(DatabaseInterface):
@staticmethod
def dump(database_name):
from trytond.tools import exec_pg_command_pipe
from trytond.tools import exec_command_pipe
cmd = ['pg_dump', '--format=c', '--no-owner']
if CONFIG['db_user']:
cmd.append('--username=' + CONFIG['db_user'])
if CONFIG['db_host']:
cmd.append('--host=' + CONFIG['db_host'])
if CONFIG['db_port']:
cmd.append('--port=' + CONFIG['db_port'])
env = {}
uri = parse_uri(config.get('database', 'uri'))
if uri.username:
cmd.append('--username=' + uri.username)
if uri.hostname:
cmd.append('--host=' + uri.hostname)
if uri.port:
cmd.append('--port=' + uri.port)
if uri.password:
# if db_password is set in configuration we should pass
# an environment variable PGPASSWORD to our subprocess
# see libpg documentation
env['PGPASSWORD'] = uri.password
cmd.append(database_name)
pipe = exec_pg_command_pipe(*tuple(cmd))
pipe = exec_command_pipe(*tuple(cmd), env=env)
pipe.stdin.close()
data = pipe.stdout.read()
res = pipe.wait()
@@ -127,7 +135,7 @@ class Database(DatabaseInterface):
@staticmethod
def restore(database_name, data):
from trytond.tools import exec_pg_command_pipe
from trytond.tools import exec_command_pipe
database = Database().connect()
cursor = database.cursor(autocommit=True)
@@ -136,12 +144,16 @@ class Database(DatabaseInterface):
cursor.close()
cmd = ['pg_restore', '--no-owner']
if CONFIG['db_user']:
cmd.append('--username=' + CONFIG['db_user'])
if CONFIG['db_host']:
cmd.append('--host=' + CONFIG['db_host'])
if CONFIG['db_port']:
cmd.append('--port=' + CONFIG['db_port'])
env = {}
uri = parse_uri(config.get('database', 'uri'))
if uri.username:
cmd.append('--username=' + uri.username)
if uri.hostname:
cmd.append('--host=' + uri.hostname)
if uri.port:
cmd.append('--port=' + uri.port)
if uri.password:
env['PGPASSWORD'] = uri.password
cmd.append('--dbname=' + database_name)
args2 = tuple(cmd)
@@ -153,7 +165,7 @@ class Database(DatabaseInterface):
args2.append(' ' + tmpfile)
args2 = tuple(args2)
pipe = exec_pg_command_pipe(*args2)
pipe = exec_command_pipe(*args2, env=env)
if not os.name == "nt":
pipe.stdin.write(data)
pipe.stdin.close()
@@ -175,23 +187,14 @@ class Database(DatabaseInterface):
@staticmethod
def list(cursor):
now = time.time()
timeout = int(CONFIG['session_timeout'])
timeout = config.getint('session', 'timeout')
res = Database._list_cache
if res and abs(Database._list_cache_timestamp - now) < timeout:
return res
db_user = CONFIG['db_user']
uri = parse_uri(config.get('database', 'uri'))
db_user = uri.username
if not db_user and os.name == 'posix':
db_user = pwd.getpwuid(os.getuid())[0]
if not db_user:
cursor.execute("SELECT usename "
"FROM pg_user "
"WHERE usesysid = ("
"SELECT datdba "
"FROM pg_database "
"WHERE datname = %s)",
(CONFIG["db_name"],))
res = cursor.fetchone()
db_user = res and res[0]
if db_user:
cursor.execute("SELECT datname "
"FROM pg_database "
+7 -7
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.backend.database import DatabaseInterface, CursorInterface
from trytond.config import CONFIG
from trytond.config import config
import os
from decimal import Decimal
import datetime
@@ -174,7 +174,7 @@ class Database(DatabaseInterface):
path = ':memory:'
else:
db_filename = self.database_name + '.sqlite'
path = os.path.join(CONFIG['data_path'], db_filename)
path = os.path.join(config.get('database', 'path'), db_filename)
if not os.path.isfile(path):
raise IOError('Database "%s" doesn\'t exist!' % db_filename)
if self._conn is not None:
@@ -216,7 +216,7 @@ class Database(DatabaseInterface):
else:
if os.sep in database_name:
return
path = os.path.join(CONFIG['data_path'],
path = os.path.join(config.get('database', 'path'),
database_name + '.sqlite')
with sqlite.connect(path) as conn:
cursor = conn.cursor()
@@ -229,7 +229,7 @@ class Database(DatabaseInterface):
return
if os.sep in database_name:
return
os.remove(os.path.join(CONFIG['data_path'],
os.remove(os.path.join(config.get('database', 'path'),
database_name + '.sqlite'))
@staticmethod
@@ -238,7 +238,7 @@ class Database(DatabaseInterface):
raise Exception('Unable to dump memory database!')
if os.sep in database_name:
raise Exception('Wrong database name!')
path = os.path.join(CONFIG['data_path'],
path = os.path.join(config.get('database', 'path'),
database_name + '.sqlite')
with open(path, 'rb') as file_p:
data = file_p.read()
@@ -250,7 +250,7 @@ class Database(DatabaseInterface):
raise Exception('Unable to restore memory database!')
if os.sep in database_name:
raise Exception('Wrong database name!')
path = os.path.join(CONFIG['data_path'],
path = os.path.join(config.get('database', 'path'),
database_name + '.sqlite')
if os.path.isfile(path):
raise Exception('Database already exists!')
@@ -262,7 +262,7 @@ class Database(DatabaseInterface):
res = []
listdir = [':memory:']
try:
listdir += os.listdir(CONFIG['data_path'])
listdir += os.listdir(config.get('database', 'path'))
except OSError:
pass
for db_file in listdir:
+17 -28
View File
@@ -1,12 +1,12 @@
#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 datetime
from threading import Lock
from collections import OrderedDict
from sql import Table
from sql.functions import Now
from trytond.transaction import Transaction
from trytond.config import CONFIG
from trytond import backend
__all__ = ['Cache', 'LRUDict']
@@ -74,18 +74,13 @@ class Cache(object):
@staticmethod
def clean(dbname):
if not CONFIG['multi_server']:
return
database = backend.get('Database')(dbname).connect()
cursor = database.cursor()
try:
cursor.execute('SELECT "timestamp", "name" FROM ir_cache')
with Transaction().new_cursor():
cursor = Transaction().cursor
table = Table('ir_cache')
cursor.execute(*table.select(table.timestamp, table.name))
timestamps = {}
for timestamp, name in cursor.fetchall():
timestamps[name] = timestamp
finally:
cursor.commit()
cursor.close()
for inst in Cache._cache_instance:
if inst._name in timestamps:
with inst._lock:
@@ -96,36 +91,30 @@ class Cache(object):
@staticmethod
def reset(dbname, name):
if not CONFIG['multi_server']:
return
with Cache._resets_lock:
Cache._resets.setdefault(dbname, set())
Cache._resets[dbname].add(name)
@staticmethod
def resets(dbname):
if not CONFIG['multi_server']:
return
database = backend.get('Database')(dbname).connect()
cursor = database.cursor()
try:
with Transaction().new_cursor():
cursor = Transaction().cursor
table = Table('ir_cache')
with Cache._resets_lock:
Cache._resets.setdefault(dbname, set())
for name in Cache._resets[dbname]:
cursor.execute('SELECT name FROM ir_cache WHERE name = %s',
(name,))
cursor.execute(*table.select(table.name,
where=table.name == name))
if cursor.fetchone():
# It would be better to insert only
cursor.execute('UPDATE ir_cache SET "timestamp" = %s '
'WHERE name = %s', (datetime.datetime.now(), name))
cursor.execute(*table.update([table.timestamp],
[Now()], where=table.name == name))
else:
cursor.execute('INSERT INTO ir_cache '
'("timestamp", "name") '
'VALUES (%s, %s)', (datetime.datetime.now(), name))
cursor.execute(*table.insert(
[table.timestamp, table.name],
[[Now(), name]]))
Cache._resets[dbname].clear()
finally:
cursor.commit()
cursor.close()
class LRUDict(OrderedDict):
+65 -114
View File
@@ -1,18 +1,11 @@
#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 sys
try:
import cdecimal
# Use cdecimal globally
if 'decimal' not in sys.modules:
sys.modules['decimal'] = cdecimal
except ImportError:
import decimal
sys.modules['cdecimal'] = decimal
import os
import ConfigParser
import getpass
import socket
import urlparse
__all__ = ['config', 'get_hostname', 'get_port', 'split_netloc',
'parse_listen', 'parse_uri']
def get_hostname(netloc):
@@ -24,119 +17,77 @@ def get_hostname(netloc):
return netloc
def get_port(netloc, protocol):
def get_port(netloc):
netloc = netloc.split(']')[-1]
if ':' in netloc:
return int(netloc.split(':')[1])
else:
return {
'jsonrpc': 8000,
'xmlrpc': 8069,
'webdav': 8080,
}.get(protocol)
return int(netloc.split(':')[1])
class ConfigManager(object):
def __init__(self, fname=None):
self.options = {
'jsonrpc': [('localhost', 8000)],
'ssl_jsonrpc': False,
'hostname_jsonrpc': None,
'xmlrpc': [],
'ssl_xmlrpc': False,
'jsondata_path': '/var/www/localhost/tryton',
'webdav': [],
'ssl_webdav': False,
'hostname_webdav': None,
'db_type': 'postgresql',
'db_host': False,
'db_port': False,
'db_name': False,
'db_user': False,
'db_password': False,
'db_minconn': 1,
'db_maxconn': 64,
'pg_path': None,
'admin_passwd': 'admin',
'verbose': False,
'debug_mode': False,
'pidfile': None,
'logconf': None,
'privatekey': '/etc/ssl/trytond/server.key',
'certificate': '/etc/ssl/trytond/server.pem',
'smtp_server': 'localhost',
'smtp_port': 25,
'smtp_ssl': False,
'smtp_tls': False,
'smtp_user': False,
'smtp_password': False,
'smtp_default_from_email': '%s@%s' % (
getpass.getuser(), socket.getfqdn()),
'data_path': '/var/lib/trytond',
'multi_server': False,
'session_timeout': 600,
'auto_reload': True,
'prevent_dblist': False,
'init': {},
'update': {},
'cron': True,
'unoconv': 'pipe,name=trytond;urp;StarOffice.ComponentContext',
'retry': 5,
'language': 'en_US',
}
self.configfile = None
def split_netloc(netloc):
return get_hostname(netloc).replace('*', ''), get_port(netloc)
def update_cmdline(self, cmdline_options):
self.options.update(cmdline_options)
# Verify that logging configuration is defined in an external file,
# if not the output will go to stdout
if self.options['logconf'] in ('None', 'False'):
self.options['logconf'] = False
# the same for the pidfile
if self.options['pidfile'] in ('None', 'False'):
self.options['pidfile'] = False
if self.options['data_path'] in ('None', 'False'):
self.options['data_path'] = False
def parse_listen(value):
for netloc in value.split(','):
yield split_netloc(netloc)
def update_etc(self, configfile=None):
if configfile is None:
configfile = os.environ.get('TRYTOND_CONFIG')
if not configfile:
prefixdir = os.path.abspath(os.path.normpath(os.path.join(
os.path.dirname(sys.prefix), '..')))
configfile = os.path.join(prefixdir, 'etc', 'trytond.conf')
if not os.path.isfile(configfile):
configdir = os.path.abspath(os.path.normpath(os.path.join(
os.path.dirname(__file__), '..')))
configfile = os.path.join(configdir, 'etc', 'trytond.conf')
if not os.path.isfile(configfile):
configfile = None
self.configfile = configfile
if not self.configfile:
def parse_uri(uri):
return urlparse.urlparse(uri)
class TrytonConfigParser(ConfigParser.RawConfigParser):
def __init__(self):
ConfigParser.RawConfigParser.__init__(self)
self.add_section('jsonrpc')
self.set('jsonrpc', 'listen', 'localhost:8000')
self.set('jsonrpc', 'data', '/var/www/localhost/tryton')
self.add_section('xmlrpc')
self.add_section('webdav')
self.add_section('database')
self.set('database', 'uri',
os.environ.get('TRYTOND_DATABASE_URI', 'sqlite://'))
self.set('database', 'path', '/var/lib/trytond')
self.set('database', 'list', 'True')
self.set('database', 'retry', 5)
self.set('database', 'language', 'en_US')
self.add_section('ssl')
self.add_section('email')
self.set('email', 'uri', 'smtp://localhost:25')
self.add_section('session')
self.set('session', 'timeout', 600)
self.add_section('report')
self.set('report', 'unoconv',
'pipe,name=trytond;urp;StarOffice.ComponentContext')
def update_etc(self, configfile=os.environ.get('TRYTOND_CONFIG')):
if not configfile:
return
self.read(configfile)
parser = ConfigParser.ConfigParser()
with open(self.configfile) as fp:
parser.readfp(fp)
for (name, value) in parser.items('options'):
if value == 'True' or value == 'true':
value = True
if value == 'False' or value == 'false':
value = False
if name in ('xmlrpc', 'jsonrpc', 'webdav') and value:
value = [(get_hostname(netloc).replace('*', ''),
get_port(netloc, name)) for netloc in value.split(',')]
self.options[name] = value
def get(self, section, option, default=None):
try:
return ConfigParser.RawConfigParser.get(self, section, option)
except ConfigParser.NoOptionError:
return default
def get(self, key, default=None):
return self.options.get(key, default)
def getint(self, section, option, default=None):
try:
return ConfigParser.RawConfigParser.getint(self, section, option)
except (ConfigParser.NoOptionError, TypeError):
return default
def __setitem__(self, key, value):
self.options[key] = value
def getfloat(self, section, option, default=None):
try:
return ConfigParser.RawConfigParser.getfloat(self, section, option)
except (ConfigParser.NoOptionError, TypeError):
return default
def __getitem__(self, key):
return self.options[key]
def getboolean(self, section, option, default=None):
try:
return ConfigParser.RawConfigParser.getboolean(
self, section, option)
except ConfigParser.NoOptionError:
return default
CONFIG = ConfigManager()
config = TrytonConfigParser()
+3 -3
View File
@@ -5,7 +5,7 @@ import hashlib
from sql.operators import Concat
from ..model import ModelView, ModelSQL, fields
from ..config import CONFIG
from ..config import config
from .. import backend
from ..transaction import Transaction
from ..pyson import Eval
@@ -117,7 +117,7 @@ class Attachment(ModelSQL, ModelView):
filename = self.digest
if self.collision:
filename = filename + '-' + str(self.collision)
filename = os.path.join(CONFIG['data_path'], db_name,
filename = os.path.join(config.get('database', 'path'), db_name,
filename[0:2], filename[2:4], filename)
if name == 'data_size' or format_ == 'size':
try:
@@ -140,7 +140,7 @@ class Attachment(ModelSQL, ModelView):
cursor = Transaction().cursor
table = cls.__table__()
db_name = cursor.dbname
directory = os.path.join(CONFIG['data_path'], db_name)
directory = os.path.join(config.get('database', 'path'), db_name)
if not os.path.isdir(directory):
os.makedirs(directory, 0770)
digest = hashlib.md5(value).hexdigest()
+3 -3
View File
@@ -2,7 +2,7 @@
#this repository contains the full copyright notices and license terms.
from ..model import ModelSQL, ModelSingleton, fields
from ..cache import Cache
from ..config import CONFIG
from ..config import config
__all__ = ['Configuration']
@@ -15,7 +15,7 @@ class Configuration(ModelSingleton, ModelSQL):
@staticmethod
def default_language():
return CONFIG['language']
return config.get('database', 'language')
@classmethod
def get_language(cls):
@@ -25,6 +25,6 @@ class Configuration(ModelSingleton, ModelSQL):
config = cls(1)
language = config.language
if not language:
language = CONFIG['language']
language = config.get('database', 'language')
cls._get_language_cache.set(None, language)
return language
+2 -2
View File
@@ -13,7 +13,7 @@ from ..tools import safe_eval, get_smtp_server
from ..transaction import Transaction
from ..pool import Pool
from .. import backend
from ..config import CONFIG
from ..config import config
__all__ = [
'Cron',
@@ -132,7 +132,7 @@ class Cron(ModelSQL, ModelView):
(cron.name, cron.__url__, tb_s),
raise_exception=False)
from_addr = CONFIG['smtp_default_from_email']
from_addr = config.get('email', 'from')
to_addr = cron.request_user.email
msg = MIMEText(body, _charset='utf-8')
+3 -2
View File
@@ -509,13 +509,14 @@ class ModuleInstallUpgrade(Wizard):
modules = Module.search([
('state', 'in', ['to upgrade', 'to remove', 'to install']),
])
update = [m.name for m in modules]
langs = Lang.search([
('translatable', '=', True),
])
lang = [x.code for x in langs]
transaction.cursor.commit()
if modules:
pool.init(update=True, lang=lang)
if update:
pool.init(update=update, lang=lang)
return 'done'
+1 -2
View File
@@ -10,14 +10,13 @@ from ..tools import datetime_strftime
from ..pyson import Eval, And
from ..transaction import Transaction
from ..pool import Pool
from ..config import CONFIG
from .. import backend
__all__ = [
'SequenceType', 'Sequence', 'SequenceStrict',
]
sql_sequence = CONFIG.options['db_type'] == 'postgresql'
sql_sequence = backend.name() == 'postgresql'
class SequenceType(ModelSQL, ModelView):
+3 -2
View File
@@ -8,7 +8,7 @@ import uuid
import datetime
from trytond.model import ModelSQL, fields
from trytond.config import CONFIG
from trytond.config import config
from .. import backend
from ..transaction import Transaction
@@ -45,7 +45,8 @@ class Session(ModelSQL):
def check(cls, user, key):
"Check user key and delete old one"
now = datetime.datetime.now()
timeout = datetime.timedelta(seconds=int(CONFIG['session_timeout']))
timeout = datetime.timedelta(
seconds=config.getint('session', 'timeout'))
sessions = cls.search([
('create_uid', '=', user),
])
+3 -3
View File
@@ -4,7 +4,7 @@ from sql import Query, Expression
from .field import Field, SQLType
from ...transaction import Transaction
from ...config import CONFIG
from ... import backend
class Binary(Field):
@@ -59,14 +59,14 @@ class Binary(Field):
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'postgresql' and value is not None:
import psycopg2
return psycopg2.Binary(value)
return value
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('BYTEA', 'BYTEA')
elif db_type == 'mysql':
+2 -2
View File
@@ -4,7 +4,7 @@ import warnings
from sql import Query, Expression
from ...config import CONFIG
from ... import backend
from .field import Field, FieldTranslate, size_validate, SQLType
@@ -59,7 +59,7 @@ class Char(FieldTranslate):
return value
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if self.size and db_type != 'sqlite':
return SQLType('VARCHAR', 'VARCHAR(%s)' % self.size)
elif db_type == 'mysql':
+3 -3
View File
@@ -3,7 +3,7 @@
import datetime
from sql import Query, Expression
from ...config import CONFIG
from ... import backend
from .field import Field, SQLType
@@ -71,7 +71,7 @@ class DateTime(Field):
return value.replace(microsecond=0)
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'sqlite':
return SQLType('TIMESTAMP', 'TIMESTAMP')
elif db_type == 'mysql':
@@ -106,7 +106,7 @@ class Timestamp(Field):
return value
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'sqlite':
return SQLType('TIMESTAMP', 'TIMESTAMP')
elif db_type == 'mysql':
+2 -2
View File
@@ -2,7 +2,7 @@
#this repository contains the full copyright notices and license terms.
from sql import Query, Expression
from ...config import CONFIG
from ... import backend
from .field import Field, SQLType
from ...pyson import PYSON
@@ -59,7 +59,7 @@ class Float(Field):
return float(value)
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('FLOAT8', 'FLOAT8')
elif db_type == 'mysql':
+4 -4
View File
@@ -2,7 +2,7 @@
#this repository contains the full copyright notices and license terms.
from sql import Query, Expression
from ...config import CONFIG
from ... import backend
from .field import Field, SQLType
@@ -13,7 +13,7 @@ class Integer(Field):
_type = 'integer'
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('INT4', 'INT4')
elif db_type == 'mysql':
@@ -22,7 +22,7 @@ class Integer(Field):
return SQLType('INTEGER', 'INTEGER')
def sql_format(self, value):
db_type = CONFIG['db_type']
db_type = backend.name()
if (db_type == 'sqlite'
and value is not None
and not isinstance(value, (Query, Expression))):
@@ -37,7 +37,7 @@ class BigInteger(Integer):
_type = 'biginteger'
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('INT8', 'INT8')
return super(BigInteger, self).sql_type()
+2 -2
View File
@@ -6,7 +6,7 @@ from sql.operators import Or
from .field import Field, SQLType
from ...pool import Pool
from ...config import CONFIG
from ... import backend
from ...tools import reduce_ids
from ...transaction import Transaction
@@ -86,7 +86,7 @@ class Many2One(Field):
return int(value)
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('INT4', 'INT4')
elif db_type == 'mysql':
+4 -4
View File
@@ -3,7 +3,7 @@
from decimal import Decimal
from sql import Query, Expression, Cast, Literal, Select, CombiningQuery
from ...config import CONFIG
from ... import backend
from .field import SQLType
from .float import Float
@@ -26,14 +26,14 @@ class Numeric(Float):
return value
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'mysql':
return SQLType('DECIMAL', 'DECIMAL(65, 30)')
return SQLType('NUMERIC', 'NUMERIC')
def sql_column(self, table):
column = super(Numeric, self).sql_column(table)
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'sqlite':
# Must be casted as Decimal is stored as bytes
column = Cast(column, self.sql_type().base)
@@ -41,7 +41,7 @@ class Numeric(Float):
def _domain_value(self, operator, value):
value = super(Numeric, self)._domain_value(operator, value)
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'sqlite':
if isinstance(value, (Select, CombiningQuery)):
return value
+2 -2
View File
@@ -8,7 +8,7 @@ from .field import Field, SQLType
from .char import Char
from ...transaction import Transaction
from ...pool import Pool
from ...config import CONFIG
from ... import backend
class Reference(Field):
@@ -109,7 +109,7 @@ class Reference(Field):
return Char.sql_format(value)
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'mysql':
return SQLType('CHAR', 'VARCHAR(255)')
return SQLType('VARCHAR', 'VARCHAR')
+2 -2
View File
@@ -4,7 +4,7 @@ import warnings
from sql.conditionals import Case
from ...config import CONFIG
from ... import backend
from .field import Field, SQLType
@@ -44,7 +44,7 @@ class Selection(Field):
__init__.__doc__ += Field.__init__.__doc__
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'mysql':
return SQLType('CHAR', 'VARCHAR(255)')
return SQLType('VARCHAR', 'VARCHAR')
+2 -2
View File
@@ -3,7 +3,7 @@
import hashlib
from sql import Query, Expression
from ...config import CONFIG
from ... import backend
from .field import SQLType
from .char import Char
@@ -22,7 +22,7 @@ class Sha(Char):
return super(Sha, self).sql_format(value)
def sql_type(self):
db_type = CONFIG['db_type']
db_type = backend.name()
if db_type == 'mysql':
return SQLType('CHAR', 'VARCHAR(40)')
return SQLType('VARCHAR', 'VARCHAR(40)')
+2 -2
View File
@@ -24,7 +24,7 @@ from trytond.const import OPERATORS, RECORD_CACHE_SIZE, BROWSE_FIELD_TRESHOLD
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.cache import LRUDict, freeze
from trytond.config import CONFIG
from trytond import backend
from trytond.rpc import RPC
from .modelview import ModelView
@@ -1018,7 +1018,7 @@ class ModelStorage(Model):
if (value.quantize(Decimal(str(10.0 ** -digits[1])))
!= value):
raise_user_error(value)
elif CONFIG.options['db_type'] != 'mysql':
elif backend.name() != 'mysql':
if not (round(value, digits[1]) == float(value)):
raise_user_error(value)
# validate digits
+17 -23
View File
@@ -14,7 +14,7 @@ from sql import Table
from sql.functions import Now
import trytond.tools as tools
from trytond.config import CONFIG
from trytond.config import config
from trytond.transaction import Transaction
from trytond.cache import Cache
import trytond.convert as convert
@@ -135,11 +135,11 @@ class Node(Singleton):
def get_module_info(name):
"Return the content of the tryton.cfg"
config = ConfigParser.ConfigParser()
module_config = ConfigParser.ConfigParser()
with tools.file_open(os.path.join(name, 'tryton.cfg')) as fp:
config.readfp(fp)
module_config.readfp(fp)
directory = os.path.dirname(fp.name)
info = dict(config.items('tryton'))
info = dict(module_config.items('tryton'))
info['directory'] = directory
for key in ('depends', 'extras_depend', 'xml'):
if key in info:
@@ -193,18 +193,17 @@ def create_graph(module_list):
return graph, packages, later
def is_module_to_install(module):
for kind in ('init', 'update'):
if 'all' in CONFIG[kind] and module != 'tests':
return True
elif module in CONFIG[kind]:
return True
def is_module_to_install(module, update):
if 'all' in update and module != 'tests':
return True
elif module in update:
return True
return False
def load_module_graph(graph, pool, lang=None):
def load_module_graph(graph, pool, update=None, lang=None):
if lang is None:
lang = [CONFIG['language']]
lang = [config.get('database', 'language')]
modules_todo = []
models_to_update_history = set()
logger = logging.getLogger('modules')
@@ -222,7 +221,7 @@ def load_module_graph(graph, pool, lang=None):
logger.info(module)
classes = pool.setup(module)
package_state = module2state.get(module, 'uninstalled')
if (is_module_to_install(module)
if (is_module_to_install(module, update)
or package_state in ('to install', 'to upgrade')):
if package_state not in ('to install', 'to upgrade'):
if package_state == 'installed':
@@ -362,7 +361,7 @@ def register_classes():
MODULES.append(module)
def load_modules(database_name, pool, update=False, lang=None):
def load_modules(database_name, pool, update=None, lang=None):
res = True
def _load_modules():
@@ -372,7 +371,7 @@ def load_modules(database_name, pool, update=False, lang=None):
# Migration from 2.2: workflow module removed
cursor.execute(*ir_module.delete(
where=(ir_module.name == 'workflow')))
if 'all' in CONFIG['init']:
if 'all' in update:
cursor.execute(*ir_module.select(ir_module.name,
where=(ir_module.name != 'tests')))
else:
@@ -385,16 +384,11 @@ def load_modules(database_name, pool, update=False, lang=None):
'to remove'))))
module_list = [name for (name,) in cursor.fetchall()]
if update:
for module in CONFIG['init'].keys():
if CONFIG['init'][module]:
module_list.append(module)
for module in CONFIG['update'].keys():
if CONFIG['update'][module]:
module_list.append(module)
module_list += update
graph = create_graph(module_list)[0]
try:
load_module_graph(graph, pool, lang)
load_module_graph(graph, pool, update, lang)
except Exception:
cursor.rollback()
raise
@@ -423,6 +417,7 @@ def load_modules(database_name, pool, update=False, lang=None):
Module = pool.get('ir.module.module')
Module.update_list()
cursor.commit()
Cache.resets(database_name)
if not Transaction().cursor:
with Transaction().start(database_name, 0):
@@ -433,5 +428,4 @@ def load_modules(database_name, pool, update=False, lang=None):
Transaction().reset_context():
_load_modules()
Cache.resets(database_name)
return res
+1 -1
View File
@@ -128,7 +128,7 @@ class Pool(object):
'''
return self._locks[self.database_name]
def init(self, update=False, lang=None):
def init(self, update=None, lang=None):
'''
Init pool
Set update to proceed to update
+26 -28
View File
@@ -1,18 +1,17 @@
# -*- coding: utf-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 traceback
import logging
import time
import sys
import pydoc
from sql import Table, Flavor
from sql import Table
from trytond.pool import Pool
from trytond import security
from trytond import backend
from trytond.config import CONFIG
from trytond.config import config
from trytond.version import VERSION
from trytond.transaction import Transaction
from trytond.cache import Cache
@@ -20,6 +19,7 @@ from trytond.exceptions import UserError, UserWarning, NotLogged, \
ConcurrencyException
from trytond.rpc import RPC
logger = logging.getLogger(__name__)
ir_configuration = Table('ir_configuration')
ir_lang = Table('ir_lang')
@@ -40,12 +40,13 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
except Exception:
return False
res = security.login(database_name, user, session)
Cache.clean(database_name)
with Transaction().start(database_name, 0):
Cache.clean(database_name)
Cache.resets(database_name)
logger = logging.getLogger('dispatcher')
msg = res and 'successful login' or 'bad login or password'
logger.info('%s \'%s\' from %s:%d using %s on database \'%s\''
% (msg, user, host, port, protocol, database_name))
Cache.resets(database_name)
return res or False
elif method == 'logout':
name = security.logout(database_name, user, session)
@@ -81,7 +82,7 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
except Exception:
return False
elif method == 'list':
if CONFIG['prevent_dblist']:
if config.get('database', 'list'):
raise Exception('AccessDenied')
with Transaction().start(None, 0, close=True) as transaction:
return transaction.database.list(transaction.cursor)
@@ -121,7 +122,7 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
obj = pool.get(object_name, type=object_type)
return pydoc.getdoc(getattr(obj, method))
for count in range(int(CONFIG['retry']), -1, -1):
for count in range(config.getint('database', 'retry'), -1, -1):
try:
user = security.check(database_name, user, session)
except DatabaseOperationalError:
@@ -130,7 +131,6 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
raise
break
Cache.clean(database_name)
database_list = Pool.database_list()
pool = Pool(database_name)
if not database_name in database_list:
@@ -147,9 +147,13 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
raise UserError('Calling method %s on %s %s is not allowed!'
% (method, object_type, object_name))
for count in range(int(CONFIG['retry']), -1, -1):
exception_message = ('Exception calling %s.%s.%s from %s@%s:%d/%s' %
(object_type, object_name, method, user, host, port, database_name))
for count in range(config.getint('database', 'retry'), -1, -1):
with Transaction().start(database_name, user,
readonly=rpc.readonly) as transaction:
Cache.clean(database_name)
try:
c_args, c_kwargs, transaction.context, transaction.timestamp \
= rpc.convert(obj, *args, **kwargs)
@@ -166,23 +170,20 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
for i in inst]
if not rpc.readonly:
transaction.cursor.commit()
except DatabaseOperationalError, exception:
except DatabaseOperationalError:
transaction.cursor.rollback()
if count and not rpc.readonly:
continue
raise
except Exception, exception:
if CONFIG['verbose'] and not isinstance(exception, (
NotLogged, ConcurrencyException, UserError,
UserWarning)):
tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
logger = logging.getLogger('dispatcher')
logger.error('Exception calling method %s on '
'%s %s from %s@%s:%d/%s:\n'
% (method, object_type, object_name, user, host, port,
database_name) + tb_s)
except (NotLogged, ConcurrencyException, UserError, UserWarning):
logger.debug(exception_message, exc_info=sys.exc_info())
transaction.cursor.rollback()
raise
except Exception:
logger.error(exception_message, exc_info=sys.exc_info())
transaction.cursor.rollback()
raise
Cache.resets(database_name)
with Transaction().start(database_name, 0) as transaction:
pool = Pool(database_name)
Session = pool.get('ir.session')
@@ -193,7 +194,6 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
transaction.cursor.rollback()
else:
transaction.cursor.commit()
Cache.resets(database_name)
return result
@@ -225,7 +225,7 @@ def create(database_name, password, lang, admin_password):
transaction.cursor.commit()
pool = Pool(database_name)
pool.init(update=True, lang=[lang])
pool.init(update=['res', 'ir'], lang=[lang])
with Transaction().start(database_name, 0) as transaction:
User = pool.get('res.user')
Lang = pool.get('ir.lang')
@@ -246,9 +246,8 @@ def create(database_name, password, lang, admin_password):
transaction.cursor.commit()
res = True
except Exception:
logger.error('CREATE DB: %s failed' % (database_name,))
tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
logger.error('Exception in call: \n' + tb_s)
logger.error('CREATE DB: %s failed' % database_name,
exc_info=sys.exc_info())
raise
else:
logger.info('CREATE DB: %s' % (database_name,))
@@ -270,9 +269,8 @@ def drop(database_name, password):
Database.drop(cursor, database_name)
cursor.commit()
except Exception:
logger.error('DROP DB: %s failed' % (database_name,))
tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
logger.error('Exception in call: \n' + tb_s)
logger.error('DROP DB: %s failed' % database_name,
exc_info=sys.exc_info())
raise
else:
logger.info('DROP DB: %s' % (database_name))
+4 -7
View File
@@ -2,7 +2,7 @@
#this repository contains the full copyright notices and license terms.
from trytond.protocols.sslsocket import SSLSocket
from trytond.protocols.dispatcher import dispatch
from trytond.config import CONFIG
from trytond.config import config
from trytond.protocols.common import daemon, RegisterHandlerMixin
from trytond.exceptions import UserError, UserWarning, NotLogged, \
ConcurrencyException
@@ -157,10 +157,6 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
for path in sys.path:
tb_s = tb_s.replace(path, '')
if CONFIG['debug_mode']:
import pdb
traceb = sys.exc_info()[2]
pdb.post_mortem(traceb)
# report exception back to server
response['error'] = (str(sys.exc_value), tb_s)
@@ -229,7 +225,7 @@ class SimpleJSONRPCRequestHandler(RegisterHandlerMixin,
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = CONFIG['jsondata_path']
path = config.get('jsonrpc', 'data')
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
@@ -247,7 +243,8 @@ class SimpleJSONRPCRequestHandler(RegisterHandlerMixin,
def send_tryton_url(self, path):
self.send_response(300)
hostname = CONFIG['hostname'] or unicode(socket.getfqdn(), 'utf8')
hostname = (config.get('jsonrpc', 'hostname')
or unicode(socket.getfqdn(), 'utf8'))
hostname = '.'.join(encodings.idna.ToASCII(part) for part in
hostname.split('.'))
values = {
+3 -3
View File
@@ -1,6 +1,6 @@
#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
from trytond.config import config
def SSLSocket(socket):
@@ -8,6 +8,6 @@ def SSLSocket(socket):
import ssl
return ssl.wrap_socket(socket,
server_side=True,
certfile=CONFIG['certificate'],
keyfile=CONFIG['privatekey'],
certfile=config.get('ssl', 'certificate'),
keyfile=config.get('ssl', 'privatekey'),
ssl_version=ssl.PROTOCOL_SSLv23)
+8 -8
View File
@@ -7,7 +7,6 @@ import urlparse
import time
import urllib
import sys
import traceback
import logging
from threading import local
import xml.dom.minidom
@@ -21,7 +20,6 @@ from pywebdav.lib.davcmd import copyone, copytree, moveone, movetree, \
delone, deltree
from trytond.protocols.sslsocket import SSLSocket
from trytond.protocols.common import daemon
from trytond.config import CONFIG
from trytond.security import login
from trytond.version import PACKAGE, VERSION, WEBSITE
from trytond.tools.misc import LocalDict
@@ -36,6 +34,8 @@ domimpl = xml.dom.minidom.getDOMImplementation()
DAV_VERSION_1['version'] += ',access-control'
DAV_VERSION_2['version'] += ',access-control'
logger = logging.getLogger(__name__)
# Local int for multi-thread
class LocalInt(local):
@@ -131,12 +131,12 @@ class TrytonDAVInterface(iface.dav_interface):
self.verbose = False
def _log_exception(self, exception):
if CONFIG['verbose'] and not isinstance(exception, (
NotLogged, ConcurrencyException, UserError, UserWarning,
DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden)):
tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
logger = logging.getLogger('webdav')
logger.error('Exception:\n' + tb_s)
if isinstance(exception, (NotLogged, ConcurrencyException, UserError,
UserWarning, DAV_Error, DAV_NotFound, DAV_Secret,
DAV_Forbidden)):
logger.debug('Exception', exc_info=sys.exc_info())
else:
logger.error('Exception', exc_info=sys.exc_info())
@staticmethod
def get_dburi(uri):
+11 -15
View File
@@ -2,7 +2,6 @@
#this repository contains the full copyright notices and license terms.
from trytond.protocols.sslsocket import SSLSocket
from trytond.protocols.dispatcher import dispatch
from trytond.config import CONFIG
from trytond.protocols.common import daemon, RegisterHandlerMixin
from trytond.exceptions import UserError, UserWarning, NotLogged, \
ConcurrencyException
@@ -10,16 +9,18 @@ from trytond import security
import SimpleXMLRPCServer
import SocketServer
import xmlrpclib
import traceback
import socket
import sys
import base64
import datetime
from types import DictType
import logging
# convert decimal to float before marshalling:
from decimal import Decimal
logger = logging.getLogger(__name__)
def dump_decimal(self, value, write):
value = {'__class__': 'Decimal',
@@ -133,6 +134,7 @@ class GenericXMLRPCRequestHandler:
database_name = self.path[1:]
user = self.tryton['user']
session = self.tryton['session']
exception_message = 'Exception calling %s%s' % (method, params)
try:
try:
method_list = method.split('.')
@@ -150,21 +152,15 @@ class GenericXMLRPCRequestHandler:
return dispatch(host, port, 'XML-RPC', database_name, user,
session, object_type, object_name, method, *params)
except (NotLogged, ConcurrencyException), exception:
raise xmlrpclib.Fault(exception.code,
'\n'.join(exception.args))
logger.debug(exception_message, exc_info=sys.exc_info())
raise xmlrpclib.Fault(exception.code, str(exception))
except (UserError, UserWarning), exception:
logger.debug(exception_message, exc_info=sys.exc_info())
error, description = exception.args
raise xmlrpclib.Fault(exception.code,
'\n'.join((error,) + description))
except Exception:
tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
for path in sys.path:
tb_s = tb_s.replace(path, '')
if CONFIG['debug_mode']:
import pdb
traceb = sys.exc_info()[2]
pdb.post_mortem(traceb)
raise xmlrpclib.Fault(255, str(sys.exc_value) + '\n' + tb_s)
raise xmlrpclib.Fault(exception.code, str(exception))
except Exception, exception:
logger.error(exception_message, exc_info=sys.exc_info())
raise xmlrpclib.Fault(255, str(exception))
finally:
security.logout(database_name, user, session)
+2 -2
View File
@@ -21,7 +21,7 @@ except ImportError:
Manifest, MANIFEST = None, None
from genshi.filters import Translator
import lxml.etree
from trytond.config import CONFIG
from trytond.config import config
from trytond.pool import Pool, PoolBase
from trytond.transaction import Transaction
from trytond.url import URLMixin
@@ -295,7 +295,7 @@ class Report(URLMixin, PoolBase):
oext = FORMAT2EXT.get(output_format, output_format)
with os.fdopen(fd, 'wb+') as fp:
fp.write(data)
cmd = ['unoconv', '--connection=%s' % CONFIG['unoconv'],
cmd = ['unoconv', '--connection=%s' % config.get('report', 'unoconv'),
'-f', oext, '--stdout', path]
try:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+4 -3
View File
@@ -26,7 +26,7 @@ from .. import backend
from ..transaction import Transaction
from ..cache import Cache
from ..pool import Pool
from ..config import CONFIG
from ..config import config
from ..pyson import PYSONEncoder
from ..rpc import RPC
@@ -197,7 +197,8 @@ class User(ModelSQL, ModelView):
def get_sessions(users, name):
Session = Pool().get('ir.session')
now = datetime.datetime.now()
timeout = datetime.timedelta(seconds=int(CONFIG['session_timeout']))
timeout = datetime.timedelta(
seconds=config.getint('session', 'timeout'))
result = dict((u.id, 0) for u in users)
for sub_ids in grouped_slice(users):
with Transaction().set_user(0):
@@ -553,7 +554,7 @@ class LoginAttempt(ModelSQL):
@staticmethod
def delay():
return (datetime.datetime.now()
- datetime.timedelta(seconds=int(CONFIG['session_timeout'])))
- datetime.timedelta(seconds=config.getint('session', 'timeout')))
@classmethod
def add(cls, login):
+10 -4
View File
@@ -1,7 +1,13 @@
#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 os
try:
import crypt
except ImportError:
pass
from trytond.pool import Pool
from trytond.config import CONFIG
from trytond.config import config
from trytond.transaction import Transaction
from trytond.exceptions import NotLogged
@@ -48,10 +54,10 @@ def logout(dbname, user, session):
def check_super(passwd):
if passwd == CONFIG['admin_passwd']:
cryptedpasswd = config.get('session', 'super_pwd')
if cryptedpasswd and crypt.crypt(passwd, cryptedpasswd) == cryptedpasswd:
return True
else:
raise Exception('AccessDenied')
raise Exception('AccessDenied')
def check(dbname, user, session):
+35 -44
View File
@@ -13,7 +13,7 @@ import time
from getpass import getpass
import threading
from trytond.config import CONFIG
from trytond.config import config, parse_listen
from trytond import backend
from trytond.pool import Pool
from trytond.monitor import monitor
@@ -24,13 +24,12 @@ class TrytonServer(object):
def __init__(self, options):
CONFIG.update_etc(options['configfile'])
CONFIG.update_cmdline(options)
config.update_etc(options.configfile)
if CONFIG['logconf']:
logging.config.fileConfig(CONFIG['logconf'])
if options.logconf:
logging.config.fileConfig(options.logconf)
logging.getLogger('server').info('using %s as logging '
'configuration file', CONFIG['logconf'])
'configuration file', options.logconf)
else:
logformat = '[%(asctime)s] %(levelname)s:%(name)s:%(message)s'
datefmt = '%a %b %d %H:%M:%S %Y'
@@ -39,19 +38,20 @@ class TrytonServer(object):
self.logger = logging.getLogger('server')
if CONFIG.configfile:
if options.configfile:
self.logger.info('using %s as configuration file'
% CONFIG.configfile)
% options.configfile)
else:
self.logger.info('using default configuration')
self.logger.info('initialising distributed objects services')
self.xmlrpcd = []
self.jsonrpcd = []
self.webdavd = []
self.options = options
def run(self):
"Run the server and never return"
update = bool(CONFIG['init'] or CONFIG['update'])
update = self.options.init + self.options.update
init = {}
signal.signal(signal.SIGINT, lambda *a: self.stop())
@@ -61,22 +61,18 @@ class TrytonServer(object):
if hasattr(signal, 'SIGUSR1'):
signal.signal(signal.SIGUSR1, lambda *a: self.restart())
if CONFIG['pidfile']:
with open(CONFIG['pidfile'], 'w') as fd_pid:
if self.options.pidfile:
with open(self.options.pidfile, 'w') as fd_pid:
fd_pid.write("%d" % (os.getpid()))
if not CONFIG["db_name"] \
and bool(CONFIG['init'] or CONFIG['update']):
raise Exception('Missing database option!')
if not update:
self.start_servers()
for db_name in CONFIG["db_name"]:
for db_name in self.options.database_names:
init[db_name] = False
with Transaction().start(db_name, 0) as transaction:
cursor = transaction.cursor
if CONFIG['init']:
if self.options.init:
if not cursor.test():
self.logger.info("init db")
backend.get('Database').init(cursor)
@@ -85,7 +81,7 @@ class TrytonServer(object):
elif not cursor.test():
raise Exception("'%s' is not a Tryton database!" % db_name)
for db_name in CONFIG["db_name"]:
for db_name in self.options.database_names:
if update:
with Transaction().start(db_name, 0) as transaction:
cursor = transaction.cursor
@@ -99,10 +95,7 @@ class TrytonServer(object):
lang = None
Pool(db_name).init(update=update, lang=lang)
for kind in ('init', 'update'):
CONFIG[kind] = {}
for db_name in CONFIG['db_name']:
for db_name in self.options.database_names:
if init[db_name]:
# try to read password from environment variable
# TRYTONPASSFILE, empty TRYTONPASSFILE ignored
@@ -145,7 +138,7 @@ class TrytonServer(object):
threads = {}
while True:
if CONFIG['cron']:
if self.options.cron:
for dbname in Pool.database_list():
thread = threads.get(dbname)
if thread and thread.is_alive():
@@ -164,7 +157,7 @@ class TrytonServer(object):
args=(dbname,), kwargs={})
thread.start()
threads[dbname] = thread
if CONFIG['auto_reload']:
if self.options.debug:
for _ in range(60):
if monitor():
self.restart()
@@ -173,33 +166,31 @@ class TrytonServer(object):
time.sleep(60)
def start_servers(self):
ssl = config.get('ssl', 'privatekey')
# Launch Server
if CONFIG['jsonrpc']:
if config.get('jsonrpc', 'listen'):
from trytond.protocols.jsonrpc import JSONRPCDaemon
for hostname, port in CONFIG['jsonrpc']:
self.jsonrpcd.append(JSONRPCDaemon(hostname, port,
CONFIG['ssl_jsonrpc']))
for hostname, port in parse_listen(
config.get('jsonrpc', 'listen')):
self.jsonrpcd.append(JSONRPCDaemon(hostname, port, ssl))
self.logger.info("starting JSON-RPC%s protocol on %s:%d" %
(CONFIG['ssl_jsonrpc'] and ' SSL' or '', hostname or '*',
port))
(ssl and ' SSL' or '', hostname or '*', port))
if CONFIG['xmlrpc']:
if config.get('xmlrpc', 'listen'):
from trytond.protocols.xmlrpc import XMLRPCDaemon
for hostname, port in CONFIG['xmlrpc']:
self.xmlrpcd.append(XMLRPCDaemon(hostname, port,
CONFIG['ssl_xmlrpc']))
for hostname, port in parse_listen(
config.get('xmlrpc', 'listen')):
self.xmlrpcd.append(XMLRPCDaemon(hostname, port, ssl))
self.logger.info("starting XML-RPC%s protocol on %s:%d" %
(CONFIG['ssl_xmlrpc'] and ' SSL' or '', hostname or '*',
port))
(ssl and ' SSL' or '', hostname or '*', port))
if CONFIG['webdav']:
if config.get('webdav', 'listen'):
from trytond.protocols.webdav import WebDAVServerThread
for hostname, port in CONFIG['webdav']:
self.webdavd.append(WebDAVServerThread(hostname, port,
CONFIG['ssl_webdav']))
for hostname, port in parse_listen(
config.get('webdav', 'listen')):
self.webdavd.append(WebDAVServerThread(hostname, port, ssl))
self.logger.info("starting WebDAV%s protocol on %s:%d" %
(CONFIG['ssl_webdav'] and ' SSL' or '', hostname or '*',
port))
(ssl and ' SSL' or '', hostname or '*', port))
for servers in (self.xmlrpcd, self.jsonrpcd, self.webdavd):
for server in servers:
@@ -211,8 +202,8 @@ class TrytonServer(object):
server.stop()
server.join()
if exit:
if CONFIG['pidfile']:
os.unlink(CONFIG['pidfile'])
if self.options.pidfile:
os.unlink(self.options.pidfile)
logging.getLogger('server').info('stopped')
logging.shutdown()
sys.exit(0)
+7 -7
View File
@@ -8,7 +8,8 @@ import time
import unittest
import sys
from trytond.config import CONFIG
from trytond.config import config
from trytond import backend
if __name__ != '__main__':
raise ImportError('%s can not be imported' % __name__)
@@ -22,18 +23,17 @@ parser.add_argument("-m", "--modules", action="store_true", dest="modules",
parser.add_argument("-v", action="count", default=0, dest="verbosity",
help="Increase verbosity")
parser.add_argument('tests', metavar='test', nargs='*')
parser.epilog = ('The database name can be specified in the DB_NAME '
'environment variable.')
opt = parser.parse_args()
CONFIG['db_type'] = 'sqlite'
CONFIG.update_etc(opt.config)
if not CONFIG['admin_passwd']:
CONFIG['admin_passwd'] = 'admin'
config.update_etc(opt.config)
if CONFIG['db_type'] == 'sqlite':
if backend.name() == 'sqlite':
database_name = ':memory:'
else:
database_name = 'test_' + str(int(time.time()))
os.environ['DB_NAME'] = database_name
os.environ.setdefault('DB_NAME', database_name)
from trytond.tests.test_tryton import all_suite, modules_suite
if not opt.modules:
+4 -4
View File
@@ -7,7 +7,7 @@ from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
install_module
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.config import CONFIG
from trytond import backend
class HistoryTestCase(unittest.TestCase):
@@ -76,7 +76,7 @@ class HistoryTestCase(unittest.TestCase):
with Transaction().set_context(_datetime=datetime.datetime.min):
self.assertRaises(UserError, History.read, [history_id])
@unittest.skipIf(CONFIG['db_type'] in ('sqlite', 'mysql'),
@unittest.skipIf(backend.name() in ('sqlite', 'mysql'),
'now() is not the start of the transaction')
def test0020read_same_timestamp(self):
'Test read history with same timestamp'
@@ -184,7 +184,7 @@ class HistoryTestCase(unittest.TestCase):
History.restore_history([history_id], datetime.datetime.min)
self.assertRaises(UserError, History.read, [history_id])
@unittest.skipIf(CONFIG['db_type'] in ('sqlite', 'mysql'),
@unittest.skipIf(backend.name() in ('sqlite', 'mysql'),
'now() is not the start of the transaction')
def test0045restore_history_same_timestamp(self):
'Test restore history with same timestamp'
@@ -287,7 +287,7 @@ class HistoryTestCase(unittest.TestCase):
records = History.search([], order=order)
self.assertEqual(records, instances)
@unittest.skipIf(CONFIG['db_type'] in ('sqlite', 'mysql'),
@unittest.skipIf(backend.name() in ('sqlite', 'mysql'),
'now() is not the start of the transaction')
def test0060_ordered_search_same_timestamp(self):
'Test ordered search with same timestamp'
+3 -3
View File
@@ -5,7 +5,7 @@
import unittest
import time
from trytond.config import CONFIG
from trytond import backend
from trytond.exceptions import UserError, ConcurrencyException
from trytond.transaction import Transaction
from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
@@ -20,7 +20,7 @@ class ModelSQLTestCase(unittest.TestCase):
self.modelsql = POOL.get('test.modelsql')
self.modelsql_timestamp = POOL.get('test.modelsql.timestamp')
@unittest.skipIf(CONFIG['db_type'] == 'sqlite',
@unittest.skipIf(backend.name == 'sqlite',
'SQLite not concerned because tryton don\'t set "NOT NULL"'
'constraint: "ALTER TABLE" don\'t support NOT NULL constraint'
'without default value')
@@ -55,7 +55,7 @@ class ModelSQLTestCase(unittest.TestCase):
timestamp = self.modelsql_timestamp.read([record.id],
['_timestamp'])[0]['_timestamp']
if CONFIG['db_type'] in ('sqlite', 'mysql'):
if backend.name() in ('sqlite', 'mysql'):
# timestamp precision of sqlite is the second
time.sleep(1)
+32 -20
View File
@@ -7,16 +7,17 @@ import unittest
import doctest
from lxml import etree
from trytond.config import CONFIG
from trytond.pool import Pool
from trytond import backend
from trytond.protocols.dispatcher import create
from trytond.protocols.dispatcher import create, drop
from trytond.transaction import Transaction
from trytond.pyson import PYSONEncoder, Eval
from trytond.exceptions import UserError
from trytond import security
__all__ = ['POOL', 'DB_NAME', 'USER', 'USER_PASSWORD', 'CONTEXT',
'install_module', 'test_view', 'test_depends', 'doctest_dropdb',
'install_module', 'test_view', 'test_depends',
'doctest_setup', 'doctest_teardown',
'suite', 'all_suite', 'modules_suite']
Pool.start()
@@ -27,6 +28,7 @@ DB_NAME = os.environ['DB_NAME']
DB = backend.get('Database')(DB_NAME)
Pool.test = True
POOL = Pool(DB_NAME)
security.check_super = lambda *a, **k: True
class ModelViewTestCase(unittest.TestCase):
@@ -72,13 +74,7 @@ def install_module(name):
'''
Install module for the tested database
'''
Database = backend.get('Database')
database = Database().connect()
cursor = database.cursor()
databases = database.list(cursor)
cursor.close()
if DB_NAME not in databases:
create(DB_NAME, CONFIG['admin_passwd'], 'en_US', USER_PASSWORD)
create_db()
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
Module = POOL.get('ir.module.module')
@@ -177,16 +173,32 @@ def test_depends():
list(depends - set(model._fields)), mname, fname))
def doctest_dropdb(test):
'''Remove SQLite memory database'''
from trytond.backend.sqlite.database import Database as SQLiteDatabase
database = SQLiteDatabase().connect()
cursor = database.cursor(autocommit=True)
try:
SQLiteDatabase.drop(cursor, ':memory:')
cursor.commit()
finally:
cursor.close()
def db_exist():
Database = backend.get('Database')
database = Database().connect()
cursor = database.cursor()
databases = database.list(cursor)
cursor.close()
return DB_NAME in databases
def create_db():
if not db_exist():
create(DB_NAME, None, 'en_US', USER_PASSWORD)
def drop_db():
if db_exist():
drop(DB_NAME, None)
def drop_create():
if db_exist:
drop_db()
create_db()
doctest_setup = lambda test: drop_create()
doctest_teardown = lambda test: drop_db()
def suite():
+15 -45
View File
@@ -15,8 +15,9 @@ from array import array
from itertools import islice
from sql import Literal
from sql.operators import Or
from trytond.config import CONFIG
from trytond.const import OPERATORS
from trytond.config import config, parse_uri
from trytond.transaction import Transaction
@@ -34,42 +35,7 @@ def find_in_path(name):
return name
def find_pg_tool(name):
if CONFIG['pg_path'] and CONFIG['pg_path'] != 'None':
return os.path.join(CONFIG['pg_path'], name)
else:
return find_in_path(name)
def exec_pg_command(name, *args):
prog = find_pg_tool(name)
if not prog:
raise Exception('Couldn\'t find %s' % name)
args2 = (os.path.basename(prog),) + args
return os.spawnv(os.P_WAIT, prog, args2)
def exec_pg_command_pipe(name, *args):
prog = find_pg_tool(name)
if not prog:
raise Exception('Couldn\'t find %s' % name)
if os.name == "nt":
cmd = '"' + prog + '" ' + ' '.join(args)
else:
cmd = prog + ' ' + ' '.join(args)
# if db_password is set in configuration we should pass
# an environment variable PGPASSWORD to our subprocess
# see libpg documentation
child_env = dict(os.environ)
if CONFIG['db_password']:
child_env['PGPASSWORD'] = CONFIG['db_password']
pipe = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, env=child_env)
return pipe
def exec_command_pipe(name, *args):
def exec_command_pipe(name, *args, **kwargs):
prog = find_in_path(name)
if not prog:
raise Exception('Couldn\'t find %s' % name)
@@ -77,7 +43,11 @@ def exec_command_pipe(name, *args):
cmd = '"' + prog + '" ' + ' '.join(args)
else:
cmd = prog + ' ' + ' '.join(args)
return os.popen2(cmd, 'b')
child_env = dict(os.environ)
if kwargs.get('env'):
child_env.update(kwargs['env'])
return subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, env=child_env)
def file_open(name, mode="r", subdir='modules'):
@@ -134,17 +104,17 @@ def get_smtp_server():
:return: A SMTP instance. The quit() method must be call when all
the calls to sendmail() have been made.
"""
if CONFIG['smtp_ssl']:
smtp_server = smtplib.SMTP_SSL(CONFIG['smtp_server'],
CONFIG['smtp_port'])
uri = parse_uri(config.get('email', 'uri'))
if uri.scheme.startswith('smtps'):
smtp_server = smtplib.SMTP_SSL(uri.hostname, uri.port)
else:
smtp_server = smtplib.SMTP(CONFIG['smtp_server'], CONFIG['smtp_port'])
smtp_server = smtplib.SMTP(uri.hostname, uri.port)
if CONFIG['smtp_tls']:
if 'tls' in uri.scheme:
smtp_server.starttls()
if CONFIG['smtp_user'] and CONFIG['smtp_password']:
smtp_server.login(CONFIG['smtp_user'], CONFIG['smtp_password'])
if uri.username and uri.password:
smtp_server.login(uri.username, uri.password)
return smtp_server
+2 -2
View File
@@ -5,12 +5,12 @@ import encodings.idna
import urllib
import socket
from trytond.config import CONFIG
from trytond.config import config
from trytond.transaction import Transaction
__all__ = ['URLMixin']
HOSTNAME = (CONFIG['hostname_jsonrpc']
HOSTNAME = (config.get('jsonrpc', 'hostname')
or unicode(socket.getfqdn(), 'utf8'))
HOSTNAME = '.'.join(encodings.idna.ToASCII(part) if part else ''
for part in HOSTNAME.split('.'))
+3 -3
View File
@@ -16,7 +16,7 @@ from trytond.model import ModelView, ModelSQL, fields
from trytond.tools import reduce_ids, safe_eval
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.config import CONFIG
from trytond.config import config
from trytond.pyson import Eval
from trytond.tools import grouped_slice
@@ -26,11 +26,11 @@ __all__ = [
def get_webdav_url():
if CONFIG['ssl_webdav']:
if config.get('ssl', 'privatekey'):
protocol = 'https'
else:
protocol = 'http'
hostname = (CONFIG['hostname_webdav']
hostname = (config.get('webdav', 'hostname')
or unicode(socket.getfqdn(), 'utf8'))
hostname = '.'.join(encodings.idna.ToASCII(part) for part in
hostname.split('.'))