diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13b34f5..1273a8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,13 +21,7 @@ jobs: fail-fast: false matrix: include: - - python-version: "2.7" - toxenv: "py27-8.0" - - python-version: "2.7" - toxenv: "py27-9.0" - - python-version: "2.7" - toxenv: "py27-10.0" - - python-version: "3.5" + - python-version: "3.6" toxenv: "py35-11.0" - python-version: "3.6" toxenv: "py36-12.0" @@ -71,6 +65,7 @@ jobs: key: ${{ runner.os }}-pip-${{ matrix.toxenv }} - name: Install system dependencies run: | + sudo apt-get update -qq sudo apt-get install -qq --no-install-recommends \ libxml2-dev libxslt1-dev \ libldap2-dev libsasl2-dev \ diff --git a/.isort.cfg b/.isort.cfg index ee44721..f238bf7 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,9 +1,2 @@ [settings] -; see https://github.com/ambv/black -multi_line_output=3 -include_trailing_comma=True -force_grid_wrap=0 -combine_as_imports=True -use_parentheses=True -line_length=88 -known_third_party = click,click_odoo,mock,psycopg2,pytest,setuptools +profile = black diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 216550e..97e77c0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,36 +1,26 @@ exclude: "^tests/data/.*$" repos: - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 22.3.0 hooks: - id: black - language_version: python3 - additional_dependencies: - - "click<8.1" - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v4.2.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: debug-statements - repo: https://github.com/pycqa/flake8 - rev: 3.8.3 + rev: 4.0.1 hooks: - id: flake8 additional_dependencies: ["flake8-bugbear==20.1.4"] - repo: https://github.com/asottile/pyupgrade - rev: v2.7.2 + rev: v2.32.1 hooks: - id: pyupgrade - language_version: python3 -- repo: https://github.com/asottile/seed-isort-config - rev: v2.2.0 - hooks: - - id: seed-isort-config - language_version: python3 - repo: https://github.com/pre-commit/mirrors-isort - rev: v5.5.4 + rev: v5.10.1 hooks: - id: isort - language_version: python3 diff --git a/click_odoo_contrib/_dbutils.py b/click_odoo_contrib/_dbutils.py index 87ec980..c4e192f 100644 --- a/click_odoo_contrib/_dbutils.py +++ b/click_odoo_contrib/_dbutils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2018 ACSONE SA/NV () # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). @@ -75,10 +74,7 @@ def reset_config_parameters(dbname): (dbuuid, ...) """ with OdooEnvironment(dbname) as env: - if odoo.release.version_info < (10, 0): - env.registry("ir.config_parameter").init(env.cr, force=True) - else: - env["ir.config_parameter"].init(force=True) + env["ir.config_parameter"].init(force=True) # reset enterprise keys if exists env.cr.execute( diff --git a/click_odoo_contrib/backupdb.py b/click_odoo_contrib/backupdb.py index 7c7d3d9..d1c9a72 100644 --- a/click_odoo_contrib/backupdb.py +++ b/click_odoo_contrib/backupdb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # Copyright 2018 ACSONE SA/NV () # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). diff --git a/click_odoo_contrib/copydb.py b/click_odoo_contrib/copydb.py index 1af415b..ecf5ccd 100644 --- a/click_odoo_contrib/copydb.py +++ b/click_odoo_contrib/copydb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # Copyright 2018 ACSONE SA/NV () # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). diff --git a/click_odoo_contrib/core_addons/__init__.py b/click_odoo_contrib/core_addons/__init__.py index 71c4955..e3314ed 100644 --- a/click_odoo_contrib/core_addons/__init__.py +++ b/click_odoo_contrib/core_addons/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright © 2015-2020 ACSONE SA/NV # License LGPLv3 (http://www.gnu.org/licenses/lgpl-3.0-standalone.html) """List of Odoo official addons.""" diff --git a/click_odoo_contrib/dropdb.py b/click_odoo_contrib/dropdb.py index 7b79ffa..90e64b6 100644 --- a/click_odoo_contrib/dropdb.py +++ b/click_odoo_contrib/dropdb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # Copyright 2018 ACSONE SA/NV () # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). @@ -21,7 +20,7 @@ ) @click.argument("dbname", nargs=1) def main(env, dbname, if_exists=False): - """ Drop an Odoo database and associated file store. """ + """Drop an Odoo database and associated file store.""" if not db_exists(dbname): msg = "Database does not exist: {}".format(dbname) if if_exists: diff --git a/click_odoo_contrib/initdb.py b/click_odoo_contrib/initdb.py index 557094d..0875b67 100644 --- a/click_odoo_contrib/initdb.py +++ b/click_odoo_contrib/initdb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # Copyright 2018 ACSONE SA/NV () # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). import contextlib @@ -51,12 +50,8 @@ def _db_storage(self): # when recreating the db by copying the cached template if _odoo_version >= odoo.tools.parse_version("12"): from odoo.addons.base.models.ir_attachment import IrAttachment - elif _odoo_version >= odoo.tools.parse_version("10"): - from odoo.addons.base.ir.ir_attachment import IrAttachment else: - from openerp.addons.base.ir.ir_attachment import ( - ir_attachment as IrAttachment, - ) + from odoo.addons.base.ir.ir_attachment import IrAttachment orig = IrAttachment._storage IrAttachment._storage = _db_storage try: @@ -197,7 +192,7 @@ def _drop_db(self, dbname): ) def _find_template(self, hashsum): - """ search same prefix and hashsum, any date """ + """search same prefix and hashsum, any date""" pattern = self.prefix + "-____________-" + hashsum self.pgcr.execute( """ @@ -221,7 +216,7 @@ def _touch(self, template_name, hashsum): self._rename_db(template_name, new_template_name) def create(self, new_database, hashsum): - """ Create a new database from a cached template matching hashsum """ + """Create a new database from a cached template matching hashsum""" with self._lock(): template_name = self._find_template(hashsum) if not template_name: @@ -232,7 +227,7 @@ def create(self, new_database, hashsum): return True def add(self, new_database, hashsum): - """ Create a new cached template """ + """Create a new cached template""" with self._lock(): template_name = self._find_template(hashsum) if template_name: diff --git a/click_odoo_contrib/manifest.py b/click_odoo_contrib/manifest.py index 138a967..9b15daa 100644 --- a/click_odoo_contrib/manifest.py +++ b/click_odoo_contrib/manifest.py @@ -37,7 +37,7 @@ def read_manifest(addon_dir): def find_addons(addons_dir, installable_only=True): - """ yield (addon_name, addon_dir, manifest) """ + """yield (addon_name, addon_dir, manifest)""" for addon_name in sorted(os.listdir(addons_dir)): addon_dir = os.path.join(addons_dir, addon_name) try: diff --git a/click_odoo_contrib/restoredb.py b/click_odoo_contrib/restoredb.py index 11e8d6d..bfcf0d1 100644 --- a/click_odoo_contrib/restoredb.py +++ b/click_odoo_contrib/restoredb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # Copyright 2019 ACSONE SA/NV () # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). diff --git a/click_odoo_contrib/update.py b/click_odoo_contrib/update.py index 734ffda..be35bde 100644 --- a/click_odoo_contrib/update.py +++ b/click_odoo_contrib/update.py @@ -80,10 +80,7 @@ def run(self): """ # See https://stackoverflow.com/a/35319598/1468388 terminate_session = "SELECT pg_terminate_backend(%s)" - if odoo.release.version_info < (9, 0): - params = {"dsn": odoo.sql_db.dsn(self.database)[1]} - else: - params = odoo.sql_db.connection_info_for(self.database)[1] + params = odoo.sql_db.connection_info_for(self.database)[1] # Need a separate raw psycopg2 cursor without transactioning to avoid # weird concurrency errors; this cursor will only trigger SELECTs, and # it needs to access current Postgres server status, monitoring other diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 2a9acf1..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal = 1 diff --git a/setup.py b/setup.py index 663be4b..497d2d6 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ "click-odoo>=1.3.0", "importlib_resources ; python_version<'3.9'", ], + python_requires=">=3.6", license="LGPLv3+", author="ACSONE SA/NV", author_email="info@acsone.eu", diff --git a/tests/data/test_makepot/addon_test_makepot/models/testmodel.py b/tests/data/test_makepot/addon_test_makepot/models/testmodel.py index a5421a9..7f1971f 100644 --- a/tests/data/test_makepot/addon_test_makepot/models/testmodel.py +++ b/tests/data/test_makepot/addon_test_makepot/models/testmodel.py @@ -1,7 +1,4 @@ -try: - from odoo import models, fields -except ImportError: - from openerp import models, fields +from odoo import models, fields class TestModel(models.Model): diff --git a/tests/data/test_makepot/addon_test_makepot_2/models/testmodel.py b/tests/data/test_makepot/addon_test_makepot_2/models/testmodel.py index cd6f7e8..e20d1e3 100644 --- a/tests/data/test_makepot/addon_test_makepot_2/models/testmodel.py +++ b/tests/data/test_makepot/addon_test_makepot_2/models/testmodel.py @@ -1,7 +1,4 @@ -try: - from odoo import models, fields -except ImportError: - from openerp import models, fields +from odoo import models, fields class TestModel2(models.Model): diff --git a/tests/data/test_update/v6/addon_app/res_users.py b/tests/data/test_update/v6/addon_app/res_users.py index eb02de8..4f98d79 100644 --- a/tests/data/test_update/v6/addon_app/res_users.py +++ b/tests/data/test_update/v6/addon_app/res_users.py @@ -1,7 +1,4 @@ -try: - from odoo import models, fields -except ImportError: - from openerp import models, fields +from odoo import models, fields class ResUsers(models.Model): diff --git a/tests/data/test_update/v7/addon_app/res_users.py b/tests/data/test_update/v7/addon_app/res_users.py index eb02de8..4f98d79 100644 --- a/tests/data/test_update/v7/addon_app/res_users.py +++ b/tests/data/test_update/v7/addon_app/res_users.py @@ -1,7 +1,4 @@ -try: - from odoo import models, fields -except ImportError: - from openerp import models, fields +from odoo import models, fields class ResUsers(models.Model): diff --git a/tests/scripts/install_odoo.py b/tests/scripts/install_odoo.py index d4bc2ca..eddf306 100755 --- a/tests/scripts/install_odoo.py +++ b/tests/scripts/install_odoo.py @@ -10,17 +10,10 @@ def odoo_installed(): try: import odoo # noqa - - return True except ImportError: - # odoo < 10 - try: - import openerp # noqa - - return True - except ImportError: - # odoo not installed - return False + return False + else: + return True def odoo_cloned(): @@ -39,15 +32,10 @@ def clone_odoo(): odoo_dir, ] ) - if "PGHOST" in os.environ and odoo_branch == "8.0": - # Patch postgres connection mechanism to support connection with PGHOST - # environment variable. This patch is a backport from 9.0. - patch_path = os.path.join(os.path.dirname(__file__), "sql_db-8.patch") - subprocess.check_call(["patch", "-p1", "-i", patch_path], cwd=odoo_dir) def install_odoo(): - if odoo_branch in ["8.0", "9.0", "10.0", "11.0", "12.0", "13.0"]: + if odoo_branch in ["11.0", "12.0", "13.0"]: # setuptools 58 dropped support for 2to3, which is required # for dependencies of older Odoo versions subprocess.check_call( diff --git a/tests/scripts/sql_db-8.patch b/tests/scripts/sql_db-8.patch deleted file mode 100644 index 8b36e6c..0000000 --- a/tests/scripts/sql_db-8.patch +++ /dev/null @@ -1,127 +0,0 @@ -diff --git a/openerp/sql_db.py b/openerp/sql_db.py -index 22bed4eabbc..03f9f96e082 100644 ---- a/openerp/sql_db.py -+++ b/openerp/sql_db.py -@@ -527,7 +527,11 @@ class ConnectionPool(object): - _logger.debug(('%r ' + msg), self, *args) - - @locked -- def borrow(self, dsn): -+ def borrow(self, connection_info): -+ """ -+ :param dict connection_info: dict of psql connection keywords -+ :rtype: PsycoConnection -+ """ - # free dead and leaked connections - for i, (cnx, _) in tools.reverse_enumerate(self._connections): - if cnx.closed: -@@ -541,7 +545,7 @@ class ConnectionPool(object): - _logger.warning('%r: Free leaked connection to %r', self, cnx.dsn) - - for i, (cnx, used) in enumerate(self._connections): -- if not used and cnx._original_dsn == dsn: -+ if not used and cnx._original_dsn == connection_info: - try: - cnx.reset() - except psycopg2.OperationalError: -@@ -570,11 +574,13 @@ class ConnectionPool(object): - raise PoolError('The Connection Pool Is Full') - - try: -- result = psycopg2.connect(dsn=dsn, connection_factory=PsycoConnection) -+ result = psycopg2.connect( -+ connection_factory=PsycoConnection, -+ **connection_info) - except psycopg2.Error: - _logger.exception('Connection to the database failed') - raise -- result._original_dsn = dsn -+ result._original_dsn = connection_info - self._connections.append((result, True)) - self._debug('Create new connection') - return result -@@ -639,8 +645,17 @@ class Connection(object): - except Exception: - return False - --def dsn(db_or_uri): -- """parse the given `db_or_uri` and return a 2-tuple (dbname, uri)""" -+def connection_info_for(db_or_uri): -+ """ parse the given `db_or_uri` and return a 2-tuple (dbname, connection_params) -+ -+ Connection params are either a dictionary with a single key ``dsn`` -+ containing a connection URI, or a dictionary containing connection -+ parameter keywords which psycopg2 can build a key/value connection string -+ (dsn) from -+ -+ :param str db_or_uri: database name or postgres dsn -+ :rtype: (str, dict) -+ """ - if db_or_uri.startswith(('postgresql://', 'postgres://')): - # extract db from uri - us = urlparse.urlsplit(db_or_uri) -@@ -650,15 +665,15 @@ def dsn(db_or_uri): - db_name = us.username - else: - db_name = us.hostname -- return db_name, db_or_uri -+ return db_name, {'dsn': db_or_uri} - -- _dsn = '' -+ connection_info = {'database': db_or_uri} - for p in ('host', 'port', 'user', 'password'): - cfg = tools.config['db_' + p] - if cfg: -- _dsn += '%s=%s ' % (p, cfg) -+ connection_info[p] = cfg - -- return db_or_uri, '%sdbname=%s' % (_dsn, db_or_uri) -+ return db_or_uri, connection_info - - _Pool = None - -@@ -667,16 +682,16 @@ def db_connect(to, allow_uri=False): - if _Pool is None: - _Pool = ConnectionPool(int(tools.config['db_maxconn'])) - -- db, uri = dsn(to) -+ db, info = connection_info_for(to) - if not allow_uri and db != to: - raise ValueError('URI connections not allowed') -- return Connection(_Pool, db, uri) -+ return Connection(_Pool, db, info) - - def close_db(db_name): - """ You might want to call openerp.modules.registry.RegistryManager.delete(db_name) along this function.""" - global _Pool - if _Pool: -- _Pool.close_all(dsn(db_name)[1]) -+ _Pool.close_all(connection_info_for(db_name)[1]) - - def close_all(): - global _Pool -diff --git a/openerp/tools/config.py b/openerp/tools/config.py -index 15ae40a1dce..f1031fbb34d 100644 ---- a/openerp/tools/config.py -+++ b/openerp/tools/config.py -@@ -518,21 +518,6 @@ class configmanager(object): - if len(self.options['language']) > 5: - raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE') - -- if not self.options['db_user']: -- try: -- import getpass -- self.options['db_user'] = getpass.getuser() -- except: -- self.options['db_user'] = None -- -- die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database') -- -- if self.options['db_password']: -- if sys.platform == 'win32' and not self.options['db_host']: -- self.options['db_host'] = 'localhost' -- #if self.options['db_host']: -- # self._generate_pgpassfile() -- - if opt.save: - self.save() diff --git a/tests/test_backupdb.py b/tests/test_backupdb.py index d87cadf..fd39101 100644 --- a/tests/test_backupdb.py +++ b/tests/test_backupdb.py @@ -153,10 +153,10 @@ def tests_backupdb_no_list_db(odoodb, filestore, tmp_path): odoo_cfg = tmp_path / "odoo.cfg" odoo_cfg.write_text( textwrap.dedent( - u"""\ - [options] - list_db = False - """ + """\ + [options] + list_db = False + """ ) ) cmd = [ @@ -196,7 +196,7 @@ def tests_backupdb_force_folder(pgdb, filestore, tmp_path, manifest): def tests_backupdb_force_zip(pgdb, filestore, tmp_path, manifest): zip_path = tmp_path.joinpath("test.zip") - zip_path.write_text(u"empty") + zip_path.write_text("empty") zip_filename = zip_path.as_posix() result = CliRunner().invoke(main, ["--format=zip", TEST_DBNAME, zip_filename]) assert result.exit_code != 0 @@ -262,7 +262,7 @@ def tests_backupdb_dump(pgdb, tmp_path, no_filestore): def tests_backupdb_force_dump(pgdb, tmp_path): dump_path = tmp_path.joinpath("test.dump") - dump_path.write_text(u"empty") + dump_path.write_text("empty") dump_filename = dump_path.as_posix() result = CliRunner().invoke(main, ["--format=dump", pgdb, dump_filename]) assert result.exit_code != 0 diff --git a/tox.ini b/tox.ini index f17bd31..6a0bb96 100644 --- a/tox.ini +++ b/tox.ini @@ -7,18 +7,13 @@ envlist = py38-{15.0,master} py37-{15.0} - py36-{12.0,13.0,14.0} - py35-{11.0} - py27-{8.0,9.0,10.0} + py36-{11.0,12.0,13.0,14.0} twine_check pre_commit skip_missing_interpreters = True [testenv] commands = - 8.0: {toxinidir}/tests/scripts/install_odoo.py 8.0 {envdir}/src/odoo - 9.0: {toxinidir}/tests/scripts/install_odoo.py 9.0 {envdir}/src/odoo - 10.0: {toxinidir}/tests/scripts/install_odoo.py 10.0 {envdir}/src/odoo 11.0: {toxinidir}/tests/scripts/install_odoo.py 11.0 {envdir}/src/odoo 12.0: {toxinidir}/tests/scripts/install_odoo.py 12.0 {envdir}/src/odoo 13.0: {toxinidir}/tests/scripts/install_odoo.py 13.0 {envdir}/src/odoo