Drop support of Python 2.6
review2691002
This commit is contained in:
1 parent
4e535164c2
commit
bc6acd08ee
20 files changed
+76
-492
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Drop support of Python 2.6
|
||||
* Deprecate on_change, on_change_with, selection_change_with and autocomplete
|
||||
field arguments
|
||||
* Add fields.depends decorator
|
||||
|
||||
+19
-17
@@ -3,7 +3,7 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import sys
|
||||
import os
|
||||
import optparse
|
||||
import argparse
|
||||
|
||||
DIR = os.path.abspath(os.path.normpath(os.path.join(__file__,
|
||||
'..', '..', 'trytond')))
|
||||
@@ -17,37 +17,39 @@ from trytond.version import VERSION
|
||||
def parse_commandline():
|
||||
options = {}
|
||||
|
||||
parser = optparse.OptionParser(version=VERSION)
|
||||
parser = argparse.ArgumentParser(prog='trytond')
|
||||
|
||||
parser.add_option("-c", "--config", dest="config",
|
||||
parser.add_argument('--version', action='version',
|
||||
version='%(prog)s ' + VERSION)
|
||||
parser.add_argument("-c", "--config", dest="config",
|
||||
help="specify config file")
|
||||
parser.add_option('--debug', dest='debug_mode', action='store_true',
|
||||
parser.add_argument('--debug', dest='debug_mode', action='store_true',
|
||||
help='enable debug mode (start post-mortem debugger if exceptions'
|
||||
' occur)')
|
||||
parser.add_option("-v", "--verbose", action="store_true",
|
||||
parser.add_argument("-v", "--verbose", action="store_true",
|
||||
dest="verbose", help="enable verbose mode")
|
||||
|
||||
parser.add_option("-d", "--database", dest="db_name",
|
||||
parser.add_argument("-d", "--database", dest="db_name",
|
||||
help="specify the database name")
|
||||
parser.add_option("-i", "--init", dest="init",
|
||||
parser.add_argument("-i", "--init", dest="init",
|
||||
help="init a module (use \"all\" for all modules)")
|
||||
parser.add_option("-u", "--update", dest="update",
|
||||
parser.add_argument("-u", "--update", dest="update",
|
||||
help="update a module (use \"all\" for all modules)")
|
||||
|
||||
parser.add_option("--pidfile", dest="pidfile",
|
||||
parser.add_argument("--pidfile", dest="pidfile",
|
||||
help="file where the server pid will be stored")
|
||||
parser.add_option("--logfile", dest="logfile",
|
||||
parser.add_argument("--logfile", dest="logfile",
|
||||
help="file where the server log will be stored")
|
||||
parser.add_option("--disable-cron", dest="cron",
|
||||
parser.add_argument("--disable-cron", dest="cron",
|
||||
action="store_false", help="disable 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. '\
|
||||
'The config file can be specified in the TRYTOND_CONFIG '\
|
||||
'environment variable.'
|
||||
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. '
|
||||
'The config file can be specified in the TRYTOND_CONFIG '
|
||||
'environment variable.')
|
||||
|
||||
(opt, _) = parser.parse_args()
|
||||
opt = parser.parse_args()
|
||||
|
||||
if opt.config:
|
||||
options['configfile'] = opt.config
|
||||
|
||||
@@ -7,7 +7,7 @@ How to install Tryton
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
* Python 2.6 or later (http://www.python.org/)
|
||||
* Python 2.7 or later (http://www.python.org/)
|
||||
* lxml 2.0 or later (http://codespeak.net/lxml/)
|
||||
* relatorio 0.2.0 or later (http://relatorio.openhex.org/)
|
||||
* python-dateutil (http://labix.org/python-dateutil)
|
||||
|
||||
@@ -51,7 +51,6 @@ setup(name=PACKAGE,
|
||||
'Natural Language :: Slovenian',
|
||||
'Natural Language :: Spanish',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Topic :: Software Development :: Libraries :: Application Frameworks',
|
||||
],
|
||||
|
||||
+2
-1
@@ -2,10 +2,11 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import datetime
|
||||
from threading import Lock
|
||||
from collections import OrderedDict
|
||||
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.config import CONFIG
|
||||
from trytond import backend
|
||||
from trytond.tools import OrderedDict
|
||||
|
||||
__all__ = ['Cache', 'LRUDict']
|
||||
|
||||
|
||||
@@ -1,11 +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.
|
||||
import os
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
hashlib = None
|
||||
import md5
|
||||
import hashlib
|
||||
from sql.operators import Concat
|
||||
|
||||
from ..model import ModelView, ModelSQL, fields
|
||||
@@ -147,10 +143,7 @@ class Attachment(ModelSQL, ModelView):
|
||||
directory = os.path.join(CONFIG['data_path'], db_name)
|
||||
if not os.path.isdir(directory):
|
||||
os.makedirs(directory, 0770)
|
||||
if hashlib:
|
||||
digest = hashlib.md5(value).hexdigest()
|
||||
else:
|
||||
digest = md5.new(value).hexdigest()
|
||||
digest = hashlib.md5(value).hexdigest()
|
||||
directory = os.path.join(directory, digest[0:2], digest[2:4])
|
||||
if not os.path.isdir(directory):
|
||||
os.makedirs(directory, 0770)
|
||||
|
||||
+2
-3
@@ -1,6 +1,5 @@
|
||||
#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 contextlib
|
||||
import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import traceback
|
||||
@@ -167,8 +166,8 @@ class Cron(ModelSQL, ModelView):
|
||||
req_user = cron.request_user
|
||||
language = (req_user.language.code if req_user.language
|
||||
else Config.get_language())
|
||||
with contextlib.nested(Transaction().set_user(cron.user.id),
|
||||
Transaction().set_context(language=language)):
|
||||
with Transaction().set_user(cron.user.id), \
|
||||
Transaction().set_context(language=language):
|
||||
cls.send_error_message(cron)
|
||||
|
||||
@classmethod
|
||||
|
||||
+4
-5
@@ -1,6 +1,5 @@
|
||||
#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 contextlib
|
||||
import time
|
||||
import datetime
|
||||
|
||||
@@ -215,8 +214,8 @@ class Rule(ModelSQL, ModelView):
|
||||
clause_global = {}
|
||||
ctx = cls._get_context()
|
||||
# Use root user without context to prevent recursion
|
||||
with contextlib.nested(Transaction().set_user(0),
|
||||
Transaction().set_context(user=0)):
|
||||
with Transaction().set_user(0), \
|
||||
Transaction().set_context(user=0):
|
||||
for rule in cls.browse(ids):
|
||||
assert rule.domain, ('Rule domain empty,'
|
||||
'check if migration was done')
|
||||
@@ -255,8 +254,8 @@ class Rule(ModelSQL, ModelView):
|
||||
clause = ['AND', clause_global, clause]
|
||||
|
||||
# Use root to prevent infinite recursion
|
||||
with contextlib.nested(Transaction().set_user(0),
|
||||
Transaction().set_context(active_test=False, user=0)):
|
||||
with Transaction().set_user(0), \
|
||||
Transaction().set_context(active_test=False, user=0):
|
||||
query = obj.search(clause, order=[], query=True)
|
||||
|
||||
cls._domain_get_cache.set(key, query)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#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 contextlib
|
||||
try:
|
||||
import cStringIO as StringIO
|
||||
except ImportError:
|
||||
@@ -10,10 +9,7 @@ import polib
|
||||
import xml.dom.minidom
|
||||
from difflib import SequenceMatcher
|
||||
import os
|
||||
try:
|
||||
from hashlib import md5
|
||||
except ImportError:
|
||||
from md5 import md5
|
||||
from hashlib import md5
|
||||
from lxml import etree
|
||||
from itertools import izip
|
||||
from sql import Column
|
||||
@@ -805,8 +801,8 @@ class Translation(ModelSQL, ModelView):
|
||||
res_id = model_data.db_id
|
||||
else:
|
||||
res_id = -1
|
||||
with contextlib.nested(Transaction().set_user(0),
|
||||
Transaction().set_context(module=res_id_module)):
|
||||
with Transaction().set_user(0), \
|
||||
Transaction().set_context(module=res_id_module):
|
||||
translation, = cls.search([
|
||||
('name', '=', name),
|
||||
('res_id', '=', res_id),
|
||||
@@ -868,8 +864,8 @@ class Translation(ModelSQL, ModelView):
|
||||
translation_ids.extend(ids)
|
||||
continue
|
||||
|
||||
with contextlib.nested(Transaction().set_user(0),
|
||||
Transaction().set_context(module=module)):
|
||||
with Transaction().set_user(0), \
|
||||
Transaction().set_context(module=module):
|
||||
if not ids:
|
||||
to_create.append({
|
||||
'name': name,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#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 contextlib
|
||||
from types import NoneType
|
||||
from sql import Cast, Literal, Column, Query, Expression
|
||||
from sql.functions import Substring, Position
|
||||
@@ -68,8 +67,8 @@ class Reference(Field):
|
||||
ref_to_check[ref_model][1].append(i)
|
||||
|
||||
# Check if reference ids still exist
|
||||
with contextlib.nested(Transaction().set_context(active_test=False),
|
||||
Transaction().set_user(0)):
|
||||
with Transaction().set_context(active_test=False), \
|
||||
Transaction().set_user(0):
|
||||
for ref_model, (ref_ids, ids) in ref_to_check.iteritems():
|
||||
try:
|
||||
pool.get(ref_model)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#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 contextlib
|
||||
import re
|
||||
from functools import reduce
|
||||
from itertools import islice, izip
|
||||
@@ -359,8 +358,8 @@ class ModelSQL(ModelStorage):
|
||||
id_new = cursor.lastid()
|
||||
new_ids.append(id_new)
|
||||
except DatabaseIntegrityError, exception:
|
||||
with contextlib.nested(Transaction().new_cursor(),
|
||||
Transaction().set_user(0)):
|
||||
with Transaction().new_cursor(), \
|
||||
Transaction().set_user(0):
|
||||
cls.__raise_integrity_error(exception, values)
|
||||
raise
|
||||
|
||||
@@ -703,8 +702,8 @@ class ModelSQL(ModelStorage):
|
||||
cursor.execute(*table.update(columns, update_values,
|
||||
where=red_sql))
|
||||
except DatabaseIntegrityError, exception:
|
||||
with contextlib.nested(Transaction().new_cursor(),
|
||||
Transaction().set_user(0)):
|
||||
with Transaction().new_cursor(), \
|
||||
Transaction().set_user(0):
|
||||
cls.__raise_integrity_error(exception, values,
|
||||
values.keys())
|
||||
raise
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import datetime
|
||||
import time
|
||||
import logging
|
||||
import contextlib
|
||||
import traceback
|
||||
import sys
|
||||
import csv
|
||||
@@ -1310,9 +1309,9 @@ class ModelStorage(Model):
|
||||
model2ids = {}
|
||||
model2cache = {}
|
||||
# Read the data
|
||||
with contextlib.nested(Transaction().set_cursor(self._cursor),
|
||||
Transaction().set_user(self._user),
|
||||
Transaction().set_context(self._context)):
|
||||
with Transaction().set_cursor(self._cursor), \
|
||||
Transaction().set_user(self._user), \
|
||||
Transaction().set_context(self._context):
|
||||
if self.id in self._cache and name in self._cache[self.id]:
|
||||
# Use values from cache
|
||||
ids = islice(chain(islice(self._ids, index, None),
|
||||
@@ -1407,9 +1406,9 @@ class ModelStorage(Model):
|
||||
self._values = None
|
||||
if save_values or self.id < 0:
|
||||
try:
|
||||
with contextlib.nested(Transaction().set_cursor(self._cursor),
|
||||
Transaction().set_user(self._user),
|
||||
Transaction().set_context(self._context)):
|
||||
with Transaction().set_cursor(self._cursor), \
|
||||
Transaction().set_user(self._user), \
|
||||
Transaction().set_context(self._context):
|
||||
if self.id < 0:
|
||||
self._ids.remove(self.id)
|
||||
try:
|
||||
|
||||
@@ -4,7 +4,6 @@ import os
|
||||
import sys
|
||||
import itertools
|
||||
import logging
|
||||
import contextlib
|
||||
from functools import reduce
|
||||
import imp
|
||||
import operator
|
||||
@@ -365,13 +364,8 @@ def register_classes():
|
||||
|
||||
def load_modules(database_name, pool, update=False, lang=None):
|
||||
res = True
|
||||
if not Transaction().cursor:
|
||||
contextmanager = Transaction().start(database_name, 0)
|
||||
else:
|
||||
contextmanager = contextlib.nested(Transaction().new_cursor(),
|
||||
Transaction().set_user(0),
|
||||
Transaction().reset_context())
|
||||
with contextmanager:
|
||||
|
||||
def _load_modules():
|
||||
cursor = Transaction().cursor
|
||||
if update:
|
||||
# Migration from 2.2: workflow module removed
|
||||
@@ -428,5 +422,15 @@ def load_modules(database_name, pool, update=False, lang=None):
|
||||
Module = pool.get('ir.module.module')
|
||||
Module.update_list()
|
||||
cursor.commit()
|
||||
|
||||
if not Transaction().cursor:
|
||||
with Transaction().start(database_name, 0):
|
||||
_load_modules()
|
||||
else:
|
||||
with Transaction().new_cursor(), \
|
||||
Transaction().set_user(0), \
|
||||
Transaction().reset_context():
|
||||
_load_modules()
|
||||
|
||||
Cache.resets(database_name)
|
||||
return res
|
||||
@@ -4,12 +4,6 @@ import errno
|
||||
import os
|
||||
import socket
|
||||
import threading
|
||||
import sys
|
||||
import gzip
|
||||
try:
|
||||
import cStringIO as StringIO
|
||||
except ImportError:
|
||||
import StringIO
|
||||
from SocketServer import StreamRequestHandler
|
||||
|
||||
|
||||
@@ -57,91 +51,3 @@ class RegisterHandlerMixin:
|
||||
self.server.handlers.remove(self)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
class GZipRequestHandlerMixin:
|
||||
|
||||
if sys.version_info[:2] <= (2, 6):
|
||||
# Copy from SimpleXMLRPCServer.py with gzip encoding added
|
||||
def do_POST(self):
|
||||
"""Handles the HTTP POST request.
|
||||
|
||||
Attempts to interpret all HTTP POST requests as XML-RPC calls,
|
||||
which are forwarded to the server's _dispatch method for handling.
|
||||
"""
|
||||
|
||||
# Check that the path is legal
|
||||
if not self.is_rpc_path_valid():
|
||||
self.report_404()
|
||||
return
|
||||
|
||||
try:
|
||||
# Get arguments by reading body of request.
|
||||
# We read this in chunks to avoid straining
|
||||
# socket.read(); around the 10 or 15Mb mark, some platforms
|
||||
# begin to have problems (bug #792570).
|
||||
max_chunk_size = 10 * 1024 * 1024
|
||||
size_remaining = int(self.headers["content-length"])
|
||||
L = []
|
||||
while size_remaining:
|
||||
chunk_size = min(size_remaining, max_chunk_size)
|
||||
L.append(self.rfile.read(chunk_size))
|
||||
size_remaining -= len(L[-1])
|
||||
data = ''.join(L)
|
||||
|
||||
data = self.decode_request_content(data)
|
||||
if data is None:
|
||||
return # response has been sent
|
||||
|
||||
# In previous versions of SimpleXMLRPCServer, _dispatch
|
||||
# could be overridden in this class, instead of in
|
||||
# SimpleXMLRPCDispatcher. To maintain backwards compatibility,
|
||||
# check to see if a subclass implements _dispatch and dispatch
|
||||
# using that method if present.
|
||||
response = self.server._marshaled_dispatch(
|
||||
data, getattr(self, '_dispatch', None)
|
||||
)
|
||||
except Exception: # This should only happen if the module is buggy
|
||||
# internal error, report as HTTP server error
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
else:
|
||||
# got a valid XML RPC response
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/xml")
|
||||
|
||||
# Handle gzip encoding
|
||||
if ('gzip' in self.headers.get('Accept-Encoding',
|
||||
'').split(',')
|
||||
and len(response) > self.encode_threshold):
|
||||
buffer = StringIO.StringIO()
|
||||
output = gzip.GzipFile(mode='wb', fileobj=buffer)
|
||||
output.write(response)
|
||||
output.close()
|
||||
buffer.seek(0)
|
||||
response = buffer.getvalue()
|
||||
self.send_header('Content-Encoding', 'gzip')
|
||||
|
||||
self.send_header("Content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
|
||||
def decode_request_content(self, data):
|
||||
#support gzip encoding of request
|
||||
encoding = self.headers.get("content-encoding", "identity").lower()
|
||||
if encoding == "identity":
|
||||
return data
|
||||
if encoding == "gzip":
|
||||
f = StringIO.StringIO(data)
|
||||
gzf = gzip.GzipFile(mode="rb", fileobj=f)
|
||||
try:
|
||||
decoded = gzf.read()
|
||||
except IOError:
|
||||
self.send_response(400, "error decoding gzip content")
|
||||
f.close()
|
||||
gzf.close()
|
||||
return decoded
|
||||
else:
|
||||
self.send_response(501, "encoding %r not supported" % encoding)
|
||||
self.send_header("Content-length", "0")
|
||||
self.end_headers()
|
||||
@@ -3,8 +3,7 @@
|
||||
from trytond.protocols.sslsocket import SSLSocket
|
||||
from trytond.protocols.dispatcher import dispatch
|
||||
from trytond.config import CONFIG
|
||||
from trytond.protocols.common import daemon, GZipRequestHandlerMixin, \
|
||||
RegisterHandlerMixin
|
||||
from trytond.protocols.common import daemon, RegisterHandlerMixin
|
||||
from trytond.exceptions import UserError, UserWarning, NotLogged, \
|
||||
ConcurrencyException
|
||||
import SimpleXMLRPCServer
|
||||
@@ -157,8 +156,7 @@ class GenericJSONRPCRequestHandler:
|
||||
return res
|
||||
|
||||
|
||||
class SimpleJSONRPCRequestHandler(GZipRequestHandlerMixin,
|
||||
RegisterHandlerMixin,
|
||||
class SimpleJSONRPCRequestHandler(RegisterHandlerMixin,
|
||||
GenericJSONRPCRequestHandler,
|
||||
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler,
|
||||
SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
@@ -325,14 +323,13 @@ class SimpleThreadedJSONRPCServer(SocketServer.ThreadingMixIn,
|
||||
SimpleJSONRPCServer):
|
||||
timeout = 1
|
||||
daemon_threads = True
|
||||
disable_nagle_algorithm = True
|
||||
|
||||
def server_bind(self):
|
||||
self.socket.setsockopt(socket.SOL_SOCKET,
|
||||
socket.SO_REUSEADDR, 1)
|
||||
self.socket.setsockopt(socket.SOL_SOCKET,
|
||||
socket.SO_KEEPALIVE, 1)
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP,
|
||||
socket.TCP_NODELAY, 1)
|
||||
SimpleJSONRPCServer.server_bind(self)
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
from trytond.protocols.sslsocket import SSLSocket
|
||||
from trytond.protocols.dispatcher import dispatch
|
||||
from trytond.config import CONFIG
|
||||
from trytond.protocols.common import daemon, GZipRequestHandlerMixin, \
|
||||
RegisterHandlerMixin
|
||||
from trytond.protocols.common import daemon, RegisterHandlerMixin
|
||||
from trytond.exceptions import UserError, UserWarning, NotLogged, \
|
||||
ConcurrencyException
|
||||
from trytond import security
|
||||
@@ -145,8 +144,7 @@ class GenericXMLRPCRequestHandler:
|
||||
security.logout(database_name, user, session)
|
||||
|
||||
|
||||
class SimpleXMLRPCRequestHandler(GZipRequestHandlerMixin,
|
||||
RegisterHandlerMixin,
|
||||
class SimpleXMLRPCRequestHandler(RegisterHandlerMixin,
|
||||
GenericXMLRPCRequestHandler,
|
||||
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
|
||||
protocol_version = "HTTP/1.1"
|
||||
|
||||
@@ -2,7 +2,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.
|
||||
import logging
|
||||
import optparse
|
||||
import argparse
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
@@ -13,14 +13,15 @@ if __name__ != '__main__':
|
||||
raise ImportError('%s can not be imported' % __name__)
|
||||
|
||||
logging.basicConfig(level=logging.ERROR)
|
||||
parser = optparse.OptionParser(usage="Usage: %prog [options] [test] [...]")
|
||||
parser.add_option("-c", "--config", dest="config",
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-c", "--config", dest="config",
|
||||
help="specify config file")
|
||||
parser.add_option("-m", "--modules", action="store_true", dest="modules",
|
||||
parser.add_argument("-m", "--modules", action="store_true", dest="modules",
|
||||
default=False, help="Run also modules tests")
|
||||
parser.add_option("-v", action="count", default=0, dest="verbosity",
|
||||
parser.add_argument("-v", action="count", default=0, dest="verbosity",
|
||||
help="Increase verbosity")
|
||||
opt, modules = parser.parse_args()
|
||||
parser.add_argument('tests', metavar='test', nargs='*')
|
||||
opt = parser.parse_args()
|
||||
|
||||
CONFIG['db_type'] = 'sqlite'
|
||||
CONFIG.update_etc(opt.config)
|
||||
@@ -35,7 +36,7 @@ os.environ['DB_NAME'] = database_name
|
||||
|
||||
from trytond.tests.test_tryton import all_suite, modules_suite
|
||||
if not opt.modules:
|
||||
suite = all_suite(modules)
|
||||
suite = all_suite(opt.tests)
|
||||
else:
|
||||
suite = modules_suite(modules)
|
||||
suite = modules_suite(opt.tests)
|
||||
unittest.TextTestRunner(verbosity=opt.verbosity).run(suite)
|
||||
@@ -4,26 +4,8 @@
|
||||
import unittest
|
||||
import time
|
||||
from xmlrpclib import MAXINT
|
||||
try:
|
||||
from itertools import combinations
|
||||
except ImportError:
|
||||
def combinations(iterable, r):
|
||||
pool = tuple(iterable)
|
||||
n = len(pool)
|
||||
if r > n:
|
||||
return
|
||||
indices = range(r)
|
||||
yield tuple(pool[i] for i in indices)
|
||||
while True:
|
||||
for i in reversed(range(r)):
|
||||
if indices[i] != i + n - r:
|
||||
break
|
||||
else:
|
||||
return
|
||||
indices[i] += 1
|
||||
for j in range(i + 1, r):
|
||||
indices[j] = indices[j - 1] + 1
|
||||
yield tuple(pool[i] for i in indices)
|
||||
from itertools import combinations
|
||||
|
||||
from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
|
||||
install_module
|
||||
from trytond.tests.trigger import TRIGGER_LOGS
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
from .misc import *
|
||||
from .datetime_strftime import *
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from .ordereddict import OrderedDict
|
||||
|
||||
|
||||
class ClassProperty(property):
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
# Copyright (C) 2009 by Raymond Hettinger
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and
|
||||
# pypy.
|
||||
# Passes Python2.7's test suite and incorporates all the latest updates.
|
||||
|
||||
try:
|
||||
from thread import get_ident as _get_ident
|
||||
except ImportError:
|
||||
from dummy_thread import get_ident as _get_ident
|
||||
|
||||
try:
|
||||
from _abcoll import KeysView, ValuesView, ItemsView
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class OrderedDict(dict):
|
||||
'Dictionary that remembers insertion order'
|
||||
# An inherited dict maps keys to values.
|
||||
# The inherited dict provides __getitem__, __len__, __contains__, and get.
|
||||
# The remaining methods are order-aware.
|
||||
# Big-O running times for all methods are the same as for regular
|
||||
# dictionaries.
|
||||
|
||||
# The internal self.__map dictionary maps keys to links in a doubly linked
|
||||
# list.
|
||||
# The circular doubly linked list starts and ends with a sentinel element.
|
||||
# The sentinel element never gets deleted (this simplifies the algorithm).
|
||||
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
'''Initialize an ordered dictionary. Signature is the same as for
|
||||
regular dictionaries, but keyword arguments are not recommended
|
||||
because their insertion order is arbitrary.
|
||||
|
||||
'''
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
try:
|
||||
self.__root
|
||||
except AttributeError:
|
||||
self.__root = root = [] # sentinel node
|
||||
root[:] = [root, root, None]
|
||||
self.__map = {}
|
||||
self.__update(*args, **kwds)
|
||||
|
||||
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
|
||||
'od.__setitem__(i, y) <==> od[i]=y'
|
||||
# Setting a new item creates a new link which goes at the end of the
|
||||
# linked list, and the inherited dictionary is updated with the new
|
||||
# key/value pair.
|
||||
if key not in self:
|
||||
root = self.__root
|
||||
last = root[0]
|
||||
last[1] = root[0] = self.__map[key] = [last, root, key]
|
||||
dict_setitem(self, key, value)
|
||||
|
||||
def __delitem__(self, key, dict_delitem=dict.__delitem__):
|
||||
'od.__delitem__(y) <==> del od[y]'
|
||||
# Deleting an existing item uses self.__map to find the link which is
|
||||
# then removed by updating the links in the predecessor and successor
|
||||
# nodes.
|
||||
dict_delitem(self, key)
|
||||
link_prev, link_next, key = self.__map.pop(key)
|
||||
link_prev[1] = link_next
|
||||
link_next[0] = link_prev
|
||||
|
||||
def __iter__(self):
|
||||
'od.__iter__() <==> iter(od)'
|
||||
root = self.__root
|
||||
curr = root[1]
|
||||
while curr is not root:
|
||||
yield curr[2]
|
||||
curr = curr[1]
|
||||
|
||||
def __reversed__(self):
|
||||
'od.__reversed__() <==> reversed(od)'
|
||||
root = self.__root
|
||||
curr = root[0]
|
||||
while curr is not root:
|
||||
yield curr[2]
|
||||
curr = curr[0]
|
||||
|
||||
def clear(self):
|
||||
'od.clear() -> None. Remove all items from od.'
|
||||
try:
|
||||
for node in self.__map.itervalues():
|
||||
del node[:]
|
||||
root = self.__root
|
||||
root[:] = [root, root, None]
|
||||
self.__map.clear()
|
||||
except AttributeError:
|
||||
pass
|
||||
dict.clear(self)
|
||||
|
||||
def popitem(self, last=True):
|
||||
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
|
||||
Pairs are returned in LIFO order if last is true or FIFO order if
|
||||
false.
|
||||
|
||||
'''
|
||||
if not self:
|
||||
raise KeyError('dictionary is empty')
|
||||
root = self.__root
|
||||
if last:
|
||||
link = root[0]
|
||||
link_prev = link[0]
|
||||
link_prev[1] = root
|
||||
root[0] = link_prev
|
||||
else:
|
||||
link = root[1]
|
||||
link_next = link[1]
|
||||
root[1] = link_next
|
||||
link_next[0] = root
|
||||
key = link[2]
|
||||
del self.__map[key]
|
||||
value = dict.pop(self, key)
|
||||
return key, value
|
||||
|
||||
# -- the following methods do not depend on the internal structure --
|
||||
|
||||
def keys(self):
|
||||
'od.keys() -> list of keys in od'
|
||||
return list(self)
|
||||
|
||||
def values(self):
|
||||
'od.values() -> list of values in od'
|
||||
return [self[key] for key in self]
|
||||
|
||||
def items(self):
|
||||
'od.items() -> list of (key, value) pairs in od'
|
||||
return [(key, self[key]) for key in self]
|
||||
|
||||
def iterkeys(self):
|
||||
'od.iterkeys() -> an iterator over the keys in od'
|
||||
return iter(self)
|
||||
|
||||
def itervalues(self):
|
||||
'od.itervalues -> an iterator over the values in od'
|
||||
for k in self:
|
||||
yield self[k]
|
||||
|
||||
def iteritems(self):
|
||||
'od.iteritems -> an iterator over the (key, value) items in od'
|
||||
for k in self:
|
||||
yield (k, self[k])
|
||||
|
||||
def update(*args, **kwds):
|
||||
'''od.update(E, **F) -> None. Update od from dict/iterable E and F.
|
||||
|
||||
If E is a dict instance, does: for k in E: od[k] = E[k]
|
||||
If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
|
||||
Or if E is an iterable of items, does: for k, v in E: od[k] = v
|
||||
In either case, this is followed by: for k, v in F.items(): od[k] = v
|
||||
|
||||
'''
|
||||
if len(args) > 2:
|
||||
raise TypeError('update() takes at most 2 positional '
|
||||
'arguments (%d given)' % (len(args),))
|
||||
elif not args:
|
||||
raise TypeError('update() takes at least 1 argument (0 given)')
|
||||
self = args[0]
|
||||
# Make progressively weaker assumptions about "other"
|
||||
other = ()
|
||||
if len(args) == 2:
|
||||
other = args[1]
|
||||
if isinstance(other, dict):
|
||||
for key in other:
|
||||
self[key] = other[key]
|
||||
elif hasattr(other, 'keys'):
|
||||
for key in other.keys():
|
||||
self[key] = other[key]
|
||||
else:
|
||||
for key, value in other:
|
||||
self[key] = value
|
||||
for key, value in kwds.items():
|
||||
self[key] = value
|
||||
|
||||
# let subclasses override update without breaking __init__
|
||||
__update = update
|
||||
|
||||
__marker = object()
|
||||
|
||||
def pop(self, key, default=__marker):
|
||||
'''od.pop(k[,d]) -> v, remove specified key and return the
|
||||
corresponding value. If key is not found, d is returned if given,
|
||||
otherwise KeyError is raised.
|
||||
|
||||
'''
|
||||
if key in self:
|
||||
result = self[key]
|
||||
del self[key]
|
||||
return result
|
||||
if default is self.__marker:
|
||||
raise KeyError(key)
|
||||
return default
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
|
||||
if key in self:
|
||||
return self[key]
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def __repr__(self, _repr_running={}):
|
||||
'od.__repr__() <==> repr(od)'
|
||||
call_key = id(self), _get_ident()
|
||||
if call_key in _repr_running:
|
||||
return '...'
|
||||
_repr_running[call_key] = 1
|
||||
try:
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||
finally:
|
||||
del _repr_running[call_key]
|
||||
|
||||
def __reduce__(self):
|
||||
'Return state information for pickling'
|
||||
items = [[k, self[k]] for k in self]
|
||||
inst_dict = vars(self).copy()
|
||||
for k in vars(OrderedDict()):
|
||||
inst_dict.pop(k, None)
|
||||
if inst_dict:
|
||||
return (self.__class__, (items,), inst_dict)
|
||||
return self.__class__, (items,)
|
||||
|
||||
def copy(self):
|
||||
'od.copy() -> a shallow copy of od'
|
||||
return self.__class__(self)
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, value=None):
|
||||
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
|
||||
and values equal to v (which defaults to None).
|
||||
|
||||
'''
|
||||
d = cls()
|
||||
for key in iterable:
|
||||
d[key] = value
|
||||
return d
|
||||
|
||||
def __eq__(self, other):
|
||||
'''od.__eq__(y) <==> od==y. Comparison to another OD is
|
||||
order-sensitive while comparison to a regular mapping is
|
||||
order-insensitive.
|
||||
|
||||
'''
|
||||
if isinstance(other, OrderedDict):
|
||||
return len(self) == len(other) and self.items() == other.items()
|
||||
return dict.__eq__(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
# -- the following methods are only used in Python 2.7 --
|
||||
|
||||
def viewkeys(self):
|
||||
"od.viewkeys() -> a set-like object providing a view on od's keys"
|
||||
return KeysView(self)
|
||||
|
||||
def viewvalues(self):
|
||||
"od.viewvalues() -> an object providing a view on od's values"
|
||||
return ValuesView(self)
|
||||
|
||||
def viewitems(self):
|
||||
"od.viewitems() -> a set-like object providing a view on od's items"
|
||||
return ItemsView(self)
|
||||
Reference in new issue
Block a user