diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..28a0808
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,291 @@
+{
+ "globals": {
+ "$": false,
+ "_": false,
+ "fuzzy": false,
+ "jQuery": false,
+ "moment": false,
+ "odoo": false,
+ "openerp": false,
+ "self": false
+ },
+ "env": {
+ "browser": true
+ },
+ "rules": {
+ "no-alert": "warn",
+ "no-array-constructor": "warn",
+ "no-bitwise": "off",
+ "no-caller": "warn",
+ "no-case-declarations": "warn",
+ "no-catch-shadow": "warn",
+ "no-class-assign": "warn",
+ "no-cond-assign": "warn",
+ "no-confusing-arrow": "warn",
+ "no-console": "off",
+ "no-const-assign": "warn",
+ "no-constant-condition": "warn",
+ "no-continue": "off",
+ "no-control-regex": "warn",
+ "no-debugger": "warn",
+ "no-delete-var": "warn",
+ "no-div-regex": "warn",
+ "no-dupe-args": "warn",
+ "no-dupe-class-members": "warn",
+ "no-dupe-keys": "warn",
+ "no-duplicate-case": "warn",
+ "no-duplicate-imports": "warn",
+ "no-else-return": "warn",
+ "no-empty": "warn",
+ "no-empty-character-class": "warn",
+ "no-empty-function": "warn",
+ "no-empty-pattern": "warn",
+ "no-eq-null": "warn",
+ "no-eval": "warn",
+ "no-ex-assign": "warn",
+ "no-extend-native": "warn",
+ "no-extra-bind": "warn",
+ "no-extra-boolean-cast": "warn",
+ "no-extra-label": "warn",
+ "no-extra-parens": "warn",
+ "no-extra-semi": "warn",
+ "no-fallthrough": "warn",
+ "no-floating-decimal": "warn",
+ "no-func-assign": "warn",
+ "no-implicit-coercion": ["warn", {
+ "allow": ["~"]
+ }],
+ "no-implicit-globals": "warn",
+ "no-implied-eval": "warn",
+ "no-inline-comments": "warn",
+ "no-inner-declarations": "warn",
+ "no-invalid-regexp": "warn",
+ "no-invalid-this": "off",
+ "no-irregular-whitespace": "warn",
+ "no-iterator": "warn",
+ "no-label-var": "warn",
+ "no-labels": "warn",
+ "no-lone-blocks": "warn",
+ "no-lonely-if": "warn",
+ "no-loop-func": "off",
+ "no-magic-numbers": "off",
+ "no-mixed-operators": "warn",
+ "no-mixed-requires": "warn",
+ "no-mixed-spaces-and-tabs": "warn",
+ "no-multi-spaces": "warn",
+ "no-multi-str": "warn",
+ "no-multiple-empty-lines": "warn",
+ "no-native-reassign": "warn",
+ "no-negated-condition": "warn",
+ "no-negated-in-lhs": "warn",
+ "no-nested-ternary": "off",
+ "no-new": "warn",
+ "no-new-func": "warn",
+ "no-new-object": "warn",
+ "no-new-require": "warn",
+ "no-new-symbol": "warn",
+ "no-new-wrappers": "warn",
+ "no-obj-calls": "warn",
+ "no-octal": "warn",
+ "no-octal-escape": "warn",
+ "no-param-reassign": "warn",
+ "no-path-concat": "warn",
+ "no-plusplus": "off",
+ "no-process-env": "warn",
+ "no-process-exit": "warn",
+ "no-proto": "warn",
+ "no-prototype-builtins": "warn",
+ "no-redeclare": "warn",
+ "no-regex-spaces": "warn",
+ "no-restricted-globals": "warn",
+ "no-restricted-imports": "warn",
+ "no-restricted-modules": "warn",
+ "no-restricted-syntax": "warn",
+ "no-return-assign": "warn",
+ "no-script-url": "warn",
+ "no-self-assign": "warn",
+ "no-self-compare": "warn",
+ "no-sequences": "warn",
+ "no-shadow": "warn",
+ "no-shadow-restricted-names": "warn",
+ "no-whitespace-before-property": "warn",
+ "no-spaced-func": "warn",
+ "no-sparse-arrays": "warn",
+ "no-sync": "warn",
+ "no-tabs": "warn",
+ "no-ternary": "off",
+ "no-trailing-spaces": "warn",
+ "no-this-before-super": "warn",
+ "no-throw-literal": "warn",
+ "no-undef": "warn",
+ "no-undef-init": "warn",
+ "no-undefined": "off",
+ "no-unexpected-multiline": "warn",
+ "no-underscore-dangle": "off",
+ "no-unmodified-loop-condition": "warn",
+ "no-unneeded-ternary": "warn",
+ "no-unreachable": "warn",
+ "no-unsafe-finally": "warn",
+ "no-unused-expressions": "warn",
+ "no-unused-labels": "warn",
+ "no-unused-vars": "warn",
+ "no-use-before-define": "warn",
+ "no-useless-call": "warn",
+ "no-useless-computed-key": "warn",
+ "no-useless-concat": "warn",
+ "no-useless-constructor": "warn",
+ "no-useless-escape": "warn",
+ "no-useless-rename": "warn",
+ "no-void": "warn",
+ "no-var": "off",
+ "no-warning-comments": "off",
+ "no-with": "warn",
+ "array-bracket-spacing": "off",
+ "array-callback-return": "warn",
+ "arrow-body-style": "warn",
+ "arrow-parens": "warn",
+ "arrow-spacing": "off",
+ "accessor-pairs": "warn",
+ "block-scoped-var": "off",
+ "block-spacing": ["warn", "always"],
+ "brace-style": "warn",
+ "callback-return": "warn",
+ "camelcase": "off",
+ "capitalized-comments": ["warn", "always", {
+ "ignoreConsecutiveComments": true,
+ "ignoreInlineComments": true
+ }],
+ "comma-dangle": ["warn", "always-multiline"],
+ "comma-spacing": ["warn", {
+ "before": false,
+ "after": true
+ }],
+ "comma-style": "warn",
+ "complexity": [
+ "warn",
+ 15
+ ],
+ "computed-property-spacing": "off",
+ "consistent-return": "off",
+ "consistent-this": "off",
+ "constructor-super": "warn",
+ "curly": "warn",
+ "default-case": "off",
+ "dot-location": ["warn", "property"],
+ "dot-notation": "warn",
+ "eol-last": "warn",
+ "eqeqeq": "warn",
+ "func-names": "off",
+ "func-style": "off",
+ "generator-star-spacing": "off",
+ "global-require": "warn",
+ "guard-for-in": "off",
+ "handle-callback-err": "warn",
+ "id-blacklist": "warn",
+ "id-length": "off",
+ "id-match": "warn",
+ "indent": "warn",
+ "init-declarations": "warn",
+ "jsx-quotes": "warn",
+ "key-spacing": "off",
+ "keyword-spacing": "warn",
+ "linebreak-style": [
+ "warn",
+ "unix"
+ ],
+ "lines-around-comment": "warn",
+ "max-depth": "warn",
+ "max-len": ["warn", {
+ "code": 88,
+ "ignorePattern": "odoo\\.define\\(",
+ "tabWidth": 4
+ }],
+ "max-lines": "off",
+ "max-nested-callbacks": "warn",
+ "max-params": "off",
+ "max-statements": "off",
+ "max-statements-per-line": "warn",
+ "multiline-ternary": "off",
+ "new-cap": "off",
+ "new-parens": "warn",
+ "newline-after-var": "off",
+ "newline-before-return": "off",
+ "newline-per-chained-call": "off",
+ "object-curly-newline": ["warn", { "consistent": true }],
+ "object-curly-spacing": ["warn", "never"],
+ "object-property-newline": ["warn", {
+ "allowAllPropertiesOnSameLine": true
+ }],
+ "object-shorthand": "off",
+ "one-var": "off",
+ "one-var-declaration-per-line": "off",
+ "operator-assignment": "warn",
+ "operator-linebreak": "warn",
+ "padded-blocks": "off",
+ "prefer-arrow-callback": "off",
+ "prefer-const": "warn",
+ "prefer-reflect": "off",
+ "prefer-rest-params": "off",
+ "prefer-spread": "off",
+ "prefer-template": "off",
+ "quote-props": "off",
+ "quotes": "off",
+ "radix": "warn",
+ "require-yield": "warn",
+ "rest-spread-spacing": "off",
+ "semi": [
+ "warn",
+ "always"
+ ],
+ "semi-spacing": "warn",
+ "sort-imports": "warn",
+ "sort-vars": "off",
+ "space-before-blocks": "warn",
+ "space-before-function-paren": "warn",
+ "space-in-parens": "off",
+ "space-infix-ops": "off",
+ "space-unary-ops": "off",
+ "spaced-comment": ["warn", "always"],
+ "strict": ["warn", "function"],
+ "template-curly-spacing": "off",
+ "unicode-bom": "warn",
+ "use-isnan": "warn",
+ "valid-jsdoc": ["warn", {
+ "prefer": {
+ "arg": "param",
+ "argument": "param",
+ "augments": "extends",
+ "constructor": "class",
+ "exception": "throws",
+ "func": "function",
+ "method": "function",
+ "prop": "property",
+ "return": "returns",
+ "virtual": "abstract",
+ "yield": "yields"
+ },
+ "preferType": {
+ "array": "Array",
+ "bool": "Boolean",
+ "boolean": "Boolean",
+ "number": "Number",
+ "object": "Object",
+ "str": "String",
+ "string": "String"
+ },
+ "requireParamDescription": false,
+ "requireReturn": false,
+ "requireReturnDescription": false,
+ "requireReturnType": false
+ }],
+ "valid-typeof": "warn",
+ "vars-on-top": "off",
+ "wrap-iife": "warn",
+ "wrap-regex": "warn",
+ "yield-star-spacing": "off",
+ "yoda": "warn"
+ },
+ "parserOptions": {
+ "ecmaVersion": 2017
+ }
+}
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..44ed868
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,10 @@
+[flake8]
+max-line-length = 80
+max-complexity = 16
+# B = bugbear
+# B9 = bugbear opinionated (incl line length)
+select = C,E,F,W,B,B9
+# E203: whitespace before ':' (black behaviour)
+# E501: flake8 line length (covered by bugbear B950)
+# W503: line break before binary operator (black behaviour)
+ignore = E203,E501,W503
diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644
index 0000000..0b12f63
--- /dev/null
+++ b/.isort.cfg
@@ -0,0 +1,12 @@
+[settings]
+; see https://github.com/psf/black
+multi_line_output=3
+include_trailing_comma=True
+force_grid_wrap=0
+combine_as_imports=True
+use_parentheses=True
+line_length=88
+known_odoo=odoo
+known_odoo_addons=odoo.addons
+sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER
+known_third_party=lxml,setuptools
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..876d1e9
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,68 @@
+exclude: "^setup/|/static/lib/|/static/src/lib/"
+default_language_version:
+ python: python3
+repos:
+- repo: https://github.com/psf/black
+ rev: 19.3b0
+ hooks:
+ - id: black
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v2.3.0
+ hooks:
+ - id: trailing-whitespace
+ # exclude autogenerated files
+ exclude: /README\.rst$|\.pot?$
+ - id: end-of-file-fixer
+ # exclude autogenerated files
+ exclude: /README\.rst$|\.pot?$
+ - id: debug-statements
+ - id: flake8
+ name: flake8 except __init__.py
+ exclude: /__init__\.py$
+ additional_dependencies: ["flake8-bugbear==19.8.0"]
+ - id: flake8
+ name: flake8 only __init__.py
+ args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py
+ files: /__init__\.py$
+ additional_dependencies: ["flake8-bugbear==19.8.0"]
+ - id: fix-encoding-pragma
+ args: ["--remove"]
+ - id: check-case-conflict
+ - id: check-docstring-first
+ - id: check-executables-have-shebangs
+ - id: check-merge-conflict
+ - id: check-symlinks
+ - id: check-xml
+ - id: mixed-line-ending
+ args: ["--fix=lf"]
+- repo: https://github.com/pre-commit/mirrors-pylint
+ rev: v2.3.1
+ hooks:
+ - id: pylint
+ name: pylint with optional checks
+ args: ["--rcfile=.pylintrc", "--exit-zero"]
+ verbose: true
+ additional_dependencies: ["pylint-odoo==3.0.3"]
+ - id: pylint
+ name: pylint with mandatory checks
+ args: ["--rcfile=.pylintrc-mandatory"]
+ additional_dependencies: ["pylint-odoo==3.0.3"]
+- repo: https://github.com/asottile/pyupgrade
+ rev: v1.24.0
+ hooks:
+ - id: pyupgrade
+- repo: https://github.com/asottile/seed-isort-config
+ rev: v1.9.3
+ hooks:
+ - id: seed-isort-config
+- repo: https://github.com/pre-commit/mirrors-isort
+ rev: v4.3.21
+ hooks:
+ - id: isort
+ name: isort except __init__.py
+ exclude: /__init__\.py$
+- repo: https://github.com/pre-commit/mirrors-eslint
+ rev: v6.5.1
+ hooks:
+ - id: eslint
+ verbose: true
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..f9ddbaa
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,87 @@
+[MASTER]
+load-plugins=pylint_odoo
+score=n
+
+[ODOOLINT]
+readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
+manifest_required_authors=Odoo Community Association (OCA)
+manifest_required_keys=license
+manifest_deprecated_keys=description,active
+license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
+valid_odoo_versions=13.0
+
+[MESSAGES CONTROL]
+disable=all
+
+# This .pylintrc contains optional AND mandatory checks and is meant to be
+# loaded in an IDE to have it check everything, in the hope this will make
+# optional checks more visible to contributors who otherwise never look at a
+# green travis to see optional checks that failed.
+# .pylintrc-mandatory containing only mandatory checks is used the pre-commit
+# config as a blocking check.
+
+enable=anomalous-backslash-in-string,
+ api-one-deprecated,
+ api-one-multi-together,
+ assignment-from-none,
+ attribute-deprecated,
+ class-camelcase,
+ dangerous-default-value,
+ dangerous-view-replace-wo-priority,
+ duplicate-id-csv,
+ duplicate-key,
+ duplicate-xml-fields,
+ duplicate-xml-record-id,
+ eval-referenced,
+ eval-used,
+ incoherent-interpreter-exec-perm,
+ license-allowed,
+ manifest-author-string,
+ manifest-deprecated-key,
+ manifest-required-author,
+ manifest-required-key,
+ manifest-version-format,
+ method-compute,
+ method-inverse,
+ method-required-super,
+ method-search,
+ missing-import-error,
+ missing-manifest-dependency,
+ openerp-exception-warning,
+ pointless-statement,
+ pointless-string-statement,
+ print-used,
+ redundant-keyword-arg,
+ redundant-modulename-xml,
+ reimported,
+ relative-import,
+ return-in-init,
+ rst-syntax-error,
+ sql-injection,
+ too-few-format-args,
+ translation-field,
+ translation-required,
+ unreachable,
+ use-vim-comment,
+ wrong-tabs-instead-of-spaces,
+ xml-syntax-error,
+ # messages that do not cause the lint step to fail
+ consider-merging-classes-inherited,
+ create-user-wo-reset-password,
+ dangerous-filter-wo-user,
+ deprecated-module,
+ file-not-used,
+ invalid-commit,
+ missing-newline-extrafiles,
+ missing-readme,
+ no-utf8-coding-comment,
+ odoo-addons-relative-import,
+ old-api7-method-defined,
+ redefined-builtin,
+ too-complex,
+ unnecessary-utf8-coding-comment
+
+[REPORTS]
+msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
+output-format=colorized
+reports=no
diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory
new file mode 100644
index 0000000..7635cbb
--- /dev/null
+++ b/.pylintrc-mandatory
@@ -0,0 +1,65 @@
+[MASTER]
+load-plugins=pylint_odoo
+score=n
+
+[ODOOLINT]
+readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
+manifest_required_authors=Odoo Community Association (OCA)
+manifest_required_keys=license
+manifest_deprecated_keys=description,active
+license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
+valid_odoo_versions=13.0
+
+[MESSAGES CONTROL]
+disable=all
+
+enable=anomalous-backslash-in-string,
+ api-one-deprecated,
+ api-one-multi-together,
+ assignment-from-none,
+ attribute-deprecated,
+ class-camelcase,
+ dangerous-default-value,
+ dangerous-view-replace-wo-priority,
+ duplicate-id-csv,
+ duplicate-key,
+ duplicate-xml-fields,
+ duplicate-xml-record-id,
+ eval-referenced,
+ eval-used,
+ incoherent-interpreter-exec-perm,
+ license-allowed,
+ manifest-author-string,
+ manifest-deprecated-key,
+ manifest-required-author,
+ manifest-required-key,
+ manifest-version-format,
+ method-compute,
+ method-inverse,
+ method-required-super,
+ method-search,
+ missing-import-error,
+ missing-manifest-dependency,
+ openerp-exception-warning,
+ pointless-statement,
+ pointless-string-statement,
+ print-used,
+ redundant-keyword-arg,
+ redundant-modulename-xml,
+ reimported,
+ relative-import,
+ return-in-init,
+ rst-syntax-error,
+ sql-injection,
+ too-few-format-args,
+ translation-field,
+ translation-required,
+ unreachable,
+ use-vim-comment,
+ wrong-tabs-instead-of-spaces,
+ xml-syntax-error
+
+[REPORTS]
+msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
+output-format=colorized
+reports=no
diff --git a/.travis.yml b/.travis.yml
index 261c3d3..761b593 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,36 +1,47 @@
language: python
-sudo: false
-cache: pip
+cache:
+ directories:
+ - $HOME/.cache/pip
+ - $HOME/.cache/pre-commit
python:
- - "3.5"
+ - "3.6"
addons:
postgresql: "9.6"
apt:
packages:
- expect-dev # provides unbuffer utility
- - python-lxml # because pip installation is slow
+
+stages:
+ - linting
+ - test
+
+jobs:
+ include:
+ - stage: linting
+ name: "pre-commit"
+ before_install:
+ install: pip install pre-commit
+ script: pre-commit run --all --show-diff-on-failure
+ after_success:
+ - stage: test
+ env:
+ - TESTS="1" ODOO_REPO="odoo/odoo" MAKEPOT="1"
+ - stage: test
+ env:
+ - TESTS="1" ODOO_REPO="OCA/OCB"
env:
global:
- VERSION="13.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0"
- matrix:
- - LINT_CHECK="1"
- - TESTS="1" ODOO_REPO="OCA/OCB"
- - TESTS="1" ODOO_REPO="odoo/odoo" MAKEPOT="1"
-
-
install:
- - pip install -q unidecode
- - pip install unicodecsv
- git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly
# Requirements to test server_environment modules
- printf '[options]\n\nrunning_env = dev\n' > ${HOME}/.openerp_serverrc
- - ln -s ${TRAVIS_BUILD_DIR}/server_environment_files_sample ${TRAVIS_BUILD_DIR}/server_environment_files
script:
- travis_run_tests
diff --git a/LICENSE b/LICENSE
index 3ffc567..58777e3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -658,4 +658,4 @@ specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
-.
\ No newline at end of file
+.
diff --git a/server_environment/__manifest__.py b/server_environment/__manifest__.py
index b6872be..ef036da 100644
--- a/server_environment/__manifest__.py
+++ b/server_environment/__manifest__.py
@@ -5,18 +5,12 @@
{
"name": "server configuration environment files",
"version": "13.0.2.0.0",
- "depends": [
- "base",
- "base_sparse_field",
- ],
+ "depends": ["base", "base_sparse_field"],
"author": "Camptocamp,Odoo Community Association (OCA)",
"summary": "move some configurations out of the database",
"website": "http://github.com/OCA/server-env",
"license": "GPL-3 or any later version",
"category": "Tools",
- "data": [
- 'security/res_groups.xml',
- 'serv_config.xml',
- ],
- 'installable': True,
+ "data": ["security/res_groups.xml", "serv_config.xml"],
+ "installable": True,
}
diff --git a/server_environment/models/server_env_mixin.py b/server_environment/models/server_env_mixin.py
index 0227bfa..81868c5 100644
--- a/server_environment/models/server_env_mixin.py
+++ b/server_environment/models/server_env_mixin.py
@@ -2,12 +2,12 @@
# License GPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
-
from functools import partialmethod
from lxml import etree
from odoo import api, fields, models
+
from ..server_env import serv_config
_logger = logging.getLogger(__name__)
@@ -96,19 +96,20 @@ class ServerEnvMixin(models.AbstractModel):
``keychain.backend``.
"""
- _name = 'server.env.mixin'
- _description = 'Mixin to add server environment in existing models'
+
+ _name = "server.env.mixin"
+ _description = "Mixin to add server environment in existing models"
server_env_defaults = fields.Serialized()
_server_env_getter_mapping = {
- 'integer': 'getint',
- 'float': 'getfloat',
- 'monetary': 'getfloat',
- 'boolean': 'getboolean',
- 'char': 'get',
- 'selection': 'get',
- 'text': 'get',
+ "integer": "getint",
+ "float": "getfloat",
+ "monetary": "getfloat",
+ "boolean": "getboolean",
+ "char": "get",
+ "selection": "get",
+ "text": "get",
}
@property
@@ -180,16 +181,13 @@ class ServerEnvMixin(models.AbstractModel):
# _server_env_has_key_defined so we are sure that the value is
# either in the global or the record config
getter = getattr(serv_config, config_getter)
- if (section_name in serv_config
- and field_name in serv_config[section_name]):
+ if section_name in serv_config and field_name in serv_config[section_name]:
value = getter(section_name, field_name)
else:
value = getter(global_section_name, field_name)
except Exception:
_logger.exception(
- "error trying to read field %s in section %s",
- field_name,
- section_name,
+ "error trying to read field %s in section %s", field_name, section_name
)
return False
return value
@@ -203,34 +201,31 @@ class ServerEnvMixin(models.AbstractModel):
and field_name in serv_config[global_section_name]
)
has_config = (
- section_name in serv_config
- and field_name in serv_config[section_name]
+ section_name in serv_config and field_name in serv_config[section_name]
)
return has_global_config or has_config
def _compute_server_env_from_config(self, field_name, options):
- getter_name = options.get('getter') if options else None
+ getter_name = options.get("getter") if options else None
if not getter_name:
field_type = self._fields[field_name].type
getter_name = self._server_env_getter_mapping.get(field_type)
if not getter_name:
# if you get this message and the field is working as expected,
# you may want to add the type in _server_env_getter_mapping
- _logger.warning('server.env.mixin is used on a field of type %s, '
- 'which may not be supported properly')
- getter_name = 'get'
- value = self._server_env_read_from_config(
- field_name, getter_name
- )
+ _logger.warning(
+ "server.env.mixin is used on a field of type %s, "
+ "which may not be supported properly"
+ )
+ getter_name = "get"
+ value = self._server_env_read_from_config(field_name, getter_name)
self[field_name] = value
def _compute_server_env_from_default(self, field_name, options):
- if options and options.get('compute_default'):
- getattr(self, options['compute_default'])()
+ if options and options.get("compute_default"):
+ getattr(self, options["compute_default"])()
else:
- default_field = self._server_env_default_fieldname(
- field_name
- )
+ default_field = self._server_env_default_fieldname(field_name)
if default_field:
self[field_name] = self[default_field]
else:
@@ -248,9 +243,7 @@ class ServerEnvMixin(models.AbstractModel):
record._compute_server_env_from_config(field_name, options)
else:
- record._compute_server_env_from_default(
- field_name, options
- )
+ record._compute_server_env_from_default(field_name, options)
def _inverse_server_env(self, field_name):
options = self._server_env_fields[field_name]
@@ -262,8 +255,8 @@ class ServerEnvMixin(models.AbstractModel):
# we update the default value in database
if record[is_editable_field]:
- if options and options.get('inverse_default'):
- getattr(record, options['inverse_default'])()
+ if options and options.get("inverse_default"):
+ getattr(record, options["inverse_default"])()
elif default_field:
record[default_field] = record[field_name]
@@ -278,12 +271,8 @@ class ServerEnvMixin(models.AbstractModel):
# in ``_inverse_server_env`` it would reset the value of the field
for record in self:
for field_name in self._server_env_fields:
- is_editable_field = self._server_env_is_editable_fieldname(
- field_name
- )
- is_editable = not record._server_env_has_key_defined(
- field_name
- )
+ is_editable_field = self._server_env_is_editable_fieldname(field_name)
+ is_editable = not record._server_env_has_key_defined(field_name)
record[is_editable_field] = is_editable
def _server_env_view_set_readonly(self, view_arch):
@@ -293,37 +282,32 @@ class ServerEnvMixin(models.AbstractModel):
for elem in view_arch.findall(field_xpath % field):
# set env-computed fields to readonly if the configuration
# files have a key set for this field
- elem.set('attrs',
- str({'readonly': [(is_editable_field, '=', False)]}))
+ elem.set("attrs", str({"readonly": [(is_editable_field, "=", False)]}))
if not view_arch.findall(field_xpath % is_editable_field):
# add the _is_editable fields in the view for the 'attrs'
# domain
view_arch.append(
- etree.Element(
- 'field',
- name=is_editable_field,
- invisible="1"
- )
+ etree.Element("field", name=is_editable_field, invisible="1")
)
return view_arch
- def _fields_view_get(self, view_id=None, view_type='form', toolbar=False,
- submenu=False):
+ def _fields_view_get(
+ self, view_id=None, view_type="form", toolbar=False, submenu=False
+ ):
view_data = super()._fields_view_get(
- view_id=view_id, view_type=view_type,
- toolbar=toolbar, submenu=submenu
+ view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
)
- view_arch = etree.fromstring(view_data['arch'].encode('utf-8'))
+ view_arch = etree.fromstring(view_data["arch"].encode("utf-8"))
view_arch = self._server_env_view_set_readonly(view_arch)
- view_data['arch'] = etree.tostring(view_arch, encoding='unicode')
+ view_data["arch"] = etree.tostring(view_arch, encoding="unicode")
return view_data
def _server_env_default_fieldname(self, base_field_name):
"""Return the name of the field with default value"""
options = self._server_env_fields[base_field_name]
- if options and options.get('no_default_field'):
- return ''
- return '%s_env_default' % (base_field_name,)
+ if options and options.get("no_default_field"):
+ return ""
+ return "{}_env_default".format(base_field_name)
def _server_env_is_editable_fieldname(self, base_field_name):
"""Return the name of the field for "is editable"
@@ -331,16 +315,14 @@ class ServerEnvMixin(models.AbstractModel):
This is the field used to tell if the env-computed field can
be edited.
"""
- return '%s_env_is_editable' % (base_field_name,)
+ return "{}_env_is_editable".format(base_field_name)
def _server_env_transform_field_to_read_from_env(self, field):
"""Transform the original field in a computed field"""
- field.compute = '_compute_server_env'
+ field.compute = "_compute_server_env"
- inverse_method_name = '_inverse_server_env_%s' % field.name
- inverse_method = partialmethod(
- type(self)._inverse_server_env, field.name
- )
+ inverse_method_name = "_inverse_server_env_%s" % field.name
+ inverse_method = partialmethod(type(self)._inverse_server_env, field.name)
setattr(type(self), inverse_method_name, inverse_method)
field.inverse = inverse_method_name
field.store = False
@@ -360,7 +342,7 @@ class ServerEnvMixin(models.AbstractModel):
# (inherits), we want to override it with a new one
if fieldname not in self._fields or self._fields[fieldname].inherited:
field = fields.Boolean(
- compute='_compute_server_env_is_editable',
+ compute="_compute_server_env_is_editable",
automatic=True,
# this is required to be able to edit fields
# on new records
@@ -385,14 +367,11 @@ class ServerEnvMixin(models.AbstractModel):
if fieldname not in self._fields or self._fields[fieldname].inherited:
base_field_cls = base_field.__class__
field_args = base_field.args.copy()
- field_args.pop('_sequence', None)
- field_args.update({
- 'sparse': 'server_env_defaults',
- 'automatic': True,
- })
+ field_args.pop("_sequence", None)
+ field_args.update({"sparse": "server_env_defaults", "automatic": True})
- if hasattr(base_field, 'selection'):
- field_args['selection'] = base_field.selection
+ if hasattr(base_field, "selection"):
+ field_args["selection"] = base_field.selection
field = base_field_cls(**field_args)
self._add_field(fieldname, field)
diff --git a/server_environment/server_env.py b/server_environment/server_env.py
index a18bd22..28d11f9 100644
--- a/server_environment/server_env.py
+++ b/server_environment/server_env.py
@@ -18,13 +18,14 @@
#
##############################################################################
+import configparser
import logging
import os
-import configparser
-from lxml import etree
from itertools import chain
-from odoo import api, models, fields
+from lxml import etree
+
+from odoo import api, fields, models
from odoo.tools.config import config as system_base_config
from .system_info import get_server_environment
@@ -33,19 +34,29 @@ _logger = logging.getLogger(__name__)
try:
from odoo.addons import server_environment_files
+
_dir = os.path.dirname(server_environment_files.__file__)
except ImportError:
- _logger.info('not using server_environment_files for configuration,'
- ' no directory found')
+ _logger.info(
+ "not using server_environment_files for configuration," " no directory found"
+ )
_dir = None
-ENV_VAR_NAMES = ('SERVER_ENV_CONFIG', 'SERVER_ENV_CONFIG_SECRET')
+ENV_VAR_NAMES = ("SERVER_ENV_CONFIG", "SERVER_ENV_CONFIG_SECRET")
# Same dict as RawConfigParser._boolean_states
-_boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
- '0': False, 'no': False, 'false': False, 'off': False}
+_boolean_states = {
+ "1": True,
+ "yes": True,
+ "true": True,
+ "on": True,
+ "0": False,
+ "no": False,
+ "false": False,
+ "off": False,
+}
-if not system_base_config.get('running_env', False):
+if not system_base_config.get("running_env", False):
raise Exception(
"The parameter 'running_env' has not be set neither in base config "
"file option -c or in openerprc.\n"
@@ -56,7 +67,7 @@ if not system_base_config.get('running_env', False):
ck_path = None
if _dir:
- ck_path = os.path.join(_dir, system_base_config['running_env'])
+ ck_path = os.path.join(_dir, system_base_config["running_env"])
if not os.path.exists(ck_path):
raise Exception(
@@ -77,25 +88,29 @@ def setboolean(obj, attr, _bool=None):
# Borrowed from MarkupSafe
def _escape(s):
"""Convert the characters &<>'" in string s to HTML-safe sequences."""
- return (str(s).replace('&', '&')
- .replace('>', '>')
- .replace('<', '<')
- .replace("'", ''')
- .replace('"', '"'))
+ return (
+ str(s)
+ .replace("&", "&")
+ .replace(">", ">")
+ .replace("<", "<")
+ .replace("'", "'")
+ .replace('"', """)
+ )
def _listconf(env_path):
"""List configuration files in a folder."""
- files = [os.path.join(env_path, name)
- for name in sorted(os.listdir(env_path))
- if name.endswith('.conf')]
+ files = [
+ os.path.join(env_path, name)
+ for name in sorted(os.listdir(env_path))
+ if name.endswith(".conf")
+ ]
return files
def _load_config_from_server_env_files(config_p):
- default = os.path.join(_dir, 'default')
- running_env = os.path.join(_dir,
- system_base_config['running_env'])
+ default = os.path.join(_dir, "default")
+ running_env = os.path.join(_dir, system_base_config["running_env"])
if os.path.isdir(default):
conf_files = _listconf(default) + _listconf(running_env)
else:
@@ -104,12 +119,12 @@ def _load_config_from_server_env_files(config_p):
try:
config_p.read(conf_files)
except Exception as e:
- raise Exception('Cannot read config files "%s": %s' % (conf_files, e))
+ raise Exception('Cannot read config files "{}": {}'.format(conf_files, e))
def _load_config_from_rcfile(config_p):
config_p.read(system_base_config.rcfile)
- config_p.remove_section('options')
+ config_p.remove_section("options")
def _load_config_from_env(config_p):
@@ -120,8 +135,7 @@ def _load_config_from_env(config_p):
config_p.read_string(env_config)
except configparser.Error as err:
raise Exception(
- '%s content could not be parsed: %s'
- % (varname, err,)
+ "{} content could not be parsed: {}".format(varname, err)
)
@@ -147,13 +161,15 @@ class _Defaults(dict):
def __setitem__(self, key, value):
def func(*a):
return str(value)
+
return dict.__setitem__(self, key, func)
class ServerConfiguration(models.TransientModel):
"""Display server configuration."""
- _name = 'server.config'
- _description = 'Display server configuration'
+
+ _name = "server.config"
+ _description = "Display server configuration"
_conf_defaults = _Defaults()
@classmethod
@@ -164,20 +180,20 @@ class ServerConfiguration(models.TransientModel):
"""
ModelClass = super(ServerConfiguration, cls)._build_model(pool, cr)
ModelClass._add_columns()
- ModelClass.running_env = system_base_config['running_env']
+ ModelClass.running_env = system_base_config["running_env"]
# Only show passwords in development
- ModelClass.show_passwords = ModelClass.running_env in ('dev',)
+ ModelClass.show_passwords = ModelClass.running_env in ("dev",)
ModelClass._arch = None
ModelClass._build_osv()
return ModelClass
@classmethod
def _format_key(cls, section, key):
- return '%s_I_%s' % (section, key)
+ return "{}_I_{}".format(section, key)
@classmethod
def _format_key_display_name(cls, key_name):
- return key_name.replace('_I_', ' | ')
+ return key_name.replace("_I_", " | ")
@classmethod
def _add_columns(cls):
@@ -185,16 +201,17 @@ class ServerConfiguration(models.TransientModel):
cols = chain(
list(cls._get_base_cols().items()),
list(cls._get_env_cols().items()),
- list(cls._get_system_cols().items())
+ list(cls._get_system_cols().items()),
)
for col, value in cols:
- col_name = col.replace('.', '_')
- setattr(ServerConfiguration,
- col_name,
- fields.Char(
- string=cls._format_key_display_name(col_name),
- readonly=True)
- )
+ col_name = col.replace(".", "_")
+ setattr(
+ ServerConfiguration,
+ col_name,
+ fields.Char(
+ string=cls._format_key_display_name(col_name), readonly=True
+ ),
+ )
cls._conf_defaults[col_name] = value
@classmethod
@@ -202,7 +219,7 @@ class ServerConfiguration(models.TransientModel):
""" Compute base fields"""
res = {}
for col, item in list(system_base_config.options.items()):
- key = cls._format_key('odoo', col)
+ key = cls._format_key("odoo", col)
res[key] = item
return res
@@ -222,7 +239,7 @@ class ServerConfiguration(models.TransientModel):
""" Compute system fields"""
res = {}
for col, item in get_server_environment():
- key = cls._format_key('system', col)
+ key = cls._format_key("system", col)
res[key] = item
return res
@@ -232,17 +249,19 @@ class ServerConfiguration(models.TransientModel):
names = []
for key in sorted(items):
- names.append(key.replace('.', '_'))
- return ('' +
- ''.join(['' %
- _escape(name) for name in names]) +
- '')
+ names.append(key.replace(".", "_"))
+ return (
+ ''
+ + "".join(
+ ['' % _escape(name) for name in names]
+ )
+ + ""
+ )
@classmethod
def _build_osv(cls):
"""Build the view for the current configuration."""
- arch = ('
"
cls._arch = etree.fromstring(arch)
@api.model
- def fields_view_get(self, view_id=None, view_type='form', toolbar=False,
- submenu=False):
+ def fields_view_get(
+ self, view_id=None, view_type="form", toolbar=False, submenu=False
+ ):
"""Overwrite the default method to render the custom view."""
- res = super(ServerConfiguration, self).fields_view_get(view_id,
- view_type,
- toolbar)
- View = self.env['ir.ui.view']
- if view_type == 'form':
+ res = super().fields_view_get(view_id, view_type, toolbar)
+ View = self.env["ir.ui.view"]
+ if view_type == "form":
arch_node = self._arch
- xarch, xfields = View.postprocess_and_fields(
- self._name, arch_node, view_id)
- res['arch'] = xarch
- res['fields'] = xfields
+ xarch, xfields = View.postprocess_and_fields(self._name, arch_node, view_id)
+ res["arch"] = xarch
+ res["fields"] = xfields
return res
@api.model
@@ -291,18 +308,19 @@ class ServerConfiguration(models.TransientModel):
should be secret.
:return: list of secret keywords
"""
- secret_keys = ['passw', 'key', 'secret', 'token']
+ secret_keys = ["passw", "key", "secret", "token"]
return any(secret_key in key for secret_key in secret_keys)
@api.model
def default_get(self, fields_list):
- res = {}
+ res = super().default_get(fields_list)
if not self.env.user.has_group(
- 'server_environment.has_server_configuration_access'):
+ "server_environment.has_server_configuration_access"
+ ):
return res
for key in self._conf_defaults:
if not self.show_passwords and self._is_secret(key=key):
- res[key] = '**********'
+ res[key] = "**********"
else:
res[key] = self._conf_defaults[key]()
return res
diff --git a/server_environment/system_info.py b/server_environment/system_info.py
index 6e77f78..7b08ce1 100644
--- a/server_environment/system_info.py
+++ b/server_environment/system_info.py
@@ -28,38 +28,39 @@ from odoo.tools.config import config
def _get_output(cmd):
- bindir = config['root_path']
- p = subprocess.Popen(cmd, shell=True, cwd=bindir, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
+ bindir = config["root_path"]
+ p = subprocess.Popen(
+ cmd, shell=True, cwd=bindir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+ )
return p.communicate()[0].rstrip()
def get_server_environment():
# inspired by server/bin/service/web_services.py
try:
- rev_id = 'git:%s' % _get_output('git rev-parse HEAD')
+ rev_id = "git:%s" % _get_output("git rev-parse HEAD")
except Exception:
try:
- rev_id = 'bzr: %s' % _get_output('bzr revision-info')
+ rev_id = "bzr: %s" % _get_output("bzr revision-info")
except Exception:
- rev_id = 'Can not retrieve revison from git or bzr'
+ rev_id = "Can not retrieve revison from git or bzr"
- os_lang = '.'.join([x for x in locale.getdefaultlocale() if x])
+ os_lang = ".".join([x for x in locale.getdefaultlocale() if x])
if not os_lang:
- os_lang = 'NOT SET'
- if os.name == 'posix' and platform.system() == 'Linux':
- lsbinfo = _get_output('lsb_release -a')
+ os_lang = "NOT SET"
+ if os.name == "posix" and platform.system() == "Linux":
+ lsbinfo = _get_output("lsb_release -a")
else:
- lsbinfo = 'not lsb compliant'
+ lsbinfo = "not lsb compliant"
return (
- ('platform', platform.platform()),
- ('os.name', os.name),
- ('lsb_release', lsbinfo),
- ('release', platform.release()),
- ('version', platform.version()),
- ('architecture', platform.architecture()[0]),
- ('locale', os_lang),
- ('python', platform.python_version()),
- ('odoo', release.version),
- ('revision', rev_id),
+ ("platform", platform.platform()),
+ ("os.name", os.name),
+ ("lsb_release", lsbinfo),
+ ("release", platform.release()),
+ ("version", platform.version()),
+ ("architecture", platform.architecture()[0]),
+ ("locale", os_lang),
+ ("python", platform.python_version()),
+ ("odoo", release.version),
+ ("revision", rev_id),
)
diff --git a/server_environment/tests/common.py b/server_environment/tests/common.py
index d74a133..b145b7a 100644
--- a/server_environment/tests/common.py
+++ b/server_environment/tests/common.py
@@ -6,29 +6,27 @@ from contextlib import contextmanager
from unittest.mock import patch
from odoo.tests import common
-from odoo.addons.server_environment import server_env
from odoo.tools.config import config
-import odoo.addons.server_environment.models.server_env_mixin as \
- server_env_mixin
+import odoo.addons.server_environment.models.server_env_mixin as server_env_mixin
+from odoo.addons.server_environment import server_env
class ServerEnvironmentCase(common.SavepointCase):
-
def setUp(self):
super().setUp()
- self._original_running_env = config.get('running_env')
- config['running_env'] = 'testing'
+ self._original_running_env = config.get("running_env")
+ config["running_env"] = "testing"
def tearDown(self):
super().tearDown()
- config['running_env'] = self._original_running_env
+ config["running_env"] = self._original_running_env
@contextmanager
def set_config_dir(self, path):
original_dir = server_env._dir
if path and not os.path.isabs(path):
- path = os.path.join(os.path.dirname(__file__,), path)
+ path = os.path.join(os.path.dirname(__file__), path)
server_env._dir = path
try:
yield
@@ -39,18 +37,17 @@ class ServerEnvironmentCase(common.SavepointCase):
def set_env_variables(self, public=None, secret=None):
newkeys = {}
if public:
- newkeys['SERVER_ENV_CONFIG'] = public
+ newkeys["SERVER_ENV_CONFIG"] = public
if secret:
- newkeys['SERVER_ENV_CONFIG_SECRET'] = secret
- with patch.dict('os.environ', newkeys):
+ newkeys["SERVER_ENV_CONFIG_SECRET"] = secret
+ with patch.dict("os.environ", newkeys):
yield
@contextmanager
def load_config(self, public=None, secret=None):
original_serv_config = server_env_mixin.serv_config
try:
- with self.set_config_dir(None), \
- self.set_env_variables(public, secret):
+ with self.set_config_dir(None), self.set_env_variables(public, secret):
parser = server_env._load_config()
server_env_mixin.serv_config = parser
yield
diff --git a/server_environment/tests/test_environment_variable.py b/server_environment/tests/test_environment_variable.py
index 469bc18..1747406 100644
--- a/server_environment/tests/test_environment_variable.py
+++ b/server_environment/tests/test_environment_variable.py
@@ -3,43 +3,25 @@
from odoo.addons.server_environment import server_env
+
from .common import ServerEnvironmentCase
class TestEnvironmentVariables(ServerEnvironmentCase):
-
def test_env_variables(self):
- public = (
- "[section]\n"
- "foo=bar\n"
- "bar=baz\n"
- )
- secret = (
- "[section]\n"
- "bar=foo\n"
- "alice=bob\n"
- )
- with self.set_config_dir(None), \
- self.set_env_variables(public, secret):
+ public = "[section]\n" "foo=bar\n" "bar=baz\n"
+ secret = "[section]\n" "bar=foo\n" "alice=bob\n"
+ with self.set_config_dir(None), self.set_env_variables(public, secret):
parser = server_env._load_config()
- self.assertEqual(
- list(parser.keys()),
- ['DEFAULT', 'section']
- )
+ self.assertEqual(list(parser.keys()), ["DEFAULT", "section"])
self.assertDictEqual(
- dict(parser['section'].items()),
- {'alice': 'bob',
- 'bar': 'foo',
- 'foo': 'bar'}
+ dict(parser["section"].items()),
+ {"alice": "bob", "bar": "foo", "foo": "bar"},
)
def test_env_variables_override(self):
- public = (
- "[external_service.ftp]\n"
- "user=foo\n"
- )
- with self.set_config_dir('testfiles'), \
- self.set_env_variables(public):
+ public = "[external_service.ftp]\n" "user=foo\n"
+ with self.set_config_dir("testfiles"), self.set_env_variables(public):
parser = server_env._load_config()
- val = parser.get('external_service.ftp', 'user')
- self.assertEqual(val, 'foo')
+ val = parser.get("external_service.ftp", "user")
+ self.assertEqual(val, "foo")
diff --git a/server_environment/tests/test_server_environment.py b/server_environment/tests/test_server_environment.py
index ee33798..48df0cd 100644
--- a/server_environment/tests/test_server_environment.py
+++ b/server_environment/tests/test_server_environment.py
@@ -1,34 +1,32 @@
# Copyright 2018 Camptocamp (https://www.camptocamp.com).
# License GPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-from odoo.addons.server_environment import server_env
+from .. import server_env
from . import common
class TestEnv(common.ServerEnvironmentCase):
-
def test_view(self):
- model = self.env['server.config']
+ model = self.env["server.config"]
view = model.fields_view_get()
self.assertTrue(view)
def test_default(self):
- model = self.env['server.config']
+ model = self.env["server.config"]
rec = model.create({})
defaults = rec.default_get([])
self.assertTrue(defaults)
self.assertIsInstance(defaults, dict)
pass_checked = False
for default in defaults:
- if 'passw' in default:
- self.assertNotEqual(defaults[default],
- '**********')
+ if "passw" in default:
+ self.assertNotEqual(defaults[default], "**********")
pass_checked = True
self.assertTrue(pass_checked)
def test_value_retrival(self):
- with self.set_config_dir('testfiles'):
+ with self.set_config_dir("testfiles"):
parser = server_env._load_config()
- val = parser.get('external_service.ftp', 'user')
- self.assertEqual(val, 'testing')
- val = parser.get('external_service.ftp', 'host')
- self.assertEqual(val, 'sftp.example.com')
+ val = parser.get("external_service.ftp", "user")
+ self.assertEqual(val, "testing")
+ val = parser.get("external_service.ftp", "host")
+ self.assertEqual(val, "sftp.example.com")