commit
007e51f931
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
37
.travis.yml
37
.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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ('<group col="2" colspan="4">' +
|
||||
''.join(['<field name="%s" readonly="1"/>' %
|
||||
_escape(name) for name in names]) +
|
||||
'</group>')
|
||||
names.append(key.replace(".", "_"))
|
||||
return (
|
||||
'<group col="2" colspan="4">'
|
||||
+ "".join(
|
||||
['<field name="%s" readonly="1"/>' % _escape(name) for name in names]
|
||||
)
|
||||
+ "</group>"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _build_osv(cls):
|
||||
"""Build the view for the current configuration."""
|
||||
arch = ('<form string="Configuration Form">'
|
||||
'<notebook colspan="4">')
|
||||
arch = '<form string="Configuration Form">' '<notebook colspan="4">'
|
||||
|
||||
# Odoo server configuration
|
||||
rcfile = system_base_config.rcfile
|
||||
|
|
@ -265,23 +284,21 @@ class ServerConfiguration(models.TransientModel):
|
|||
arch += cls._group(cls._get_system_cols())
|
||||
arch += '<separator colspan="4"/></page>'
|
||||
|
||||
arch += '</notebook></form>'
|
||||
arch += "</notebook></form>"
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue