Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Allow more powerful config file #2

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Run Pytest

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
run_pytest:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]

steps:
- name: Check out repository
uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install pytest-benchmark
pip install .

- name: Run pytest
run: pytest pytest/ -v
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
build/
*.egg-info/
dist/
.venv/
.vscode
*.egg
*.eggs/
95 changes: 92 additions & 3 deletions kconfiglib.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,11 @@ def my_other_fn(kconf, name, arg_1, arg_2, ...):
#
# Public classes
#
class KconfigException(Exception):
def __init__(self, msg, file, line):
super().__init__(msg)
self.file = file
self.line = line


class Kconfig(object):
Expand Down Expand Up @@ -845,6 +850,7 @@ class Kconfig(object):
"warn_assign_undef",
"warn_to_stderr",
"warnings",
"configured_syms",
"y",

# Parsing-related
Expand Down Expand Up @@ -960,7 +966,7 @@ def _init(self, filename, warn, warn_to_stderr, encoding):
# See __init__()

self._encoding = encoding

self.configured_syms = set()
self.srctree = os.getenv("srctree", "")
# A prefix we can reliably strip from glob() results to get a filename
# relative to $srctree. relpath() can cause issues for symlinks,
Expand Down Expand Up @@ -1236,14 +1242,61 @@ def load_config(self, filename=None, replace=True, verbose=None):
# This stub only exists to make sure _warn_assign_no_prompt gets
# reenabled
try:
self._load_config(filename, replace)
if filename.endswith(".py"):
self._load_config_py(filename)
else:
self._load_config(filename, replace)
except UnicodeDecodeError as e:
_decoding_error(e, filename)
finally:
self._warn_assign_no_prompt = True

return ("Loaded" if replace else "Merged") + msg

def _load_config_py(self, filename):

assert filename.endswith(".py")
# Get directory from filename, if relative directory then take use .
base_dir = os.path.dirname(filename) or "."

config = """
import os
import traceback

def kconfig_import(rel_path):
conf_globals=globals()
global _rel_dir
full_path = os.path.join(_rel_dir, rel_path)
_rel_dir = os.path.dirname(os.path.realpath(full_path))
with open(full_path, "r") as f:
config = f.read()
try:
exec(config, conf_globals)
except KconfigException as exc:
raise exc
except Exception as exc:
tb = exc.__traceback__
lineno = traceback.extract_tb(tb)[1].lineno
exc.file = full_path
exc.line = lineno
raise KconfigException(str(exc), full_path, lineno)

"""
config += f"kconfig_import(\"{filename}\")"

conf_globals = self.syms
conf_globals["_rel_dir"] = base_dir
conf_globals.update(self.named_choices)
conf_globals['KconfigException'] = KconfigException
conf_globals["kconfig_import"] = self._load_config_py

try:
exec(config, conf_globals)
except KconfigException as exc:
self._warn(str(exc), exc.file, exc.line)
raise exc


def _load_config(self, filename, replace):
with self._open_config(filename) as f:
if replace:
Expand Down Expand Up @@ -4264,6 +4317,7 @@ class Symbol(object):
"selects",
"user_value",
"weak_rev_dep",
"_attempted_value"
)

#
Expand All @@ -4283,6 +4337,40 @@ def type(self):

return self.orig_type

@property
def val(self):
if self.type == BOOL or self.type == TRISTATE:
if self.tri_value == 2:
return True
elif self.tri_value == 0:
return False
elif self.type == INT or self.type == HEX:
return int(self.str_value)
else:
return self.str_value

@val.setter
def val(self, value):
self.kconfig.configured_syms.add(self)
self._attempted_value = value
if self.type == BOOL or self.type == TRISTATE:
if value:
self.set_value(2)
else:
self.set_value(0)
else:
self.set_value(str(value))
self.check_val()

def check_val(self):
for ref_sym in self.kconfig.configured_syms:
if ref_sym._attempted_value:
if ref_sym.val != ref_sym._attempted_value:
if ref_sym == self:
raise ValueError("Could not set {} to {} from {}".format(ref_sym.name, ref_sym._attempted_value, ref_sym.val))
else:
raise ValueError("Could not set {} to {} from {} due to {}: {}".format(ref_sym.name, ref_sym._attempted_value, ref_sym.val, self.name, self._attempted_value))

@property
def str_value(self):
"""
Expand Down Expand Up @@ -4785,8 +4873,9 @@ def __init__(self):

# - UNKNOWN == 0
# - _visited is used during tree iteration and dep. loop detection
self.orig_type = self._visited = 0

self._attempted_value = None
self.orig_type = self._visited = 0
self.nodes = []

self.defaults = []
Expand Down
9 changes: 9 additions & 0 deletions pytest/test_simple_dep/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
config FOO
bool "FOO prompt"

config BAR
bool "BAR prompt"
depends on FOO

config BAZ
bool "BAZ prompt"
2 changes: 2 additions & 0 deletions pytest/test_simple_dep/app.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FOO = y
BAR = y
4 changes: 4 additions & 0 deletions pytest/test_simple_dep/config.conditional.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
if FOO.val:
BAR.val = True
else:
FOO.val = True
1 change: 1 addition & 0 deletions pytest/test_simple_dep/config.import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kconfig_import("config.py")
1 change: 1 addition & 0 deletions pytest/test_simple_dep/config.import100.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[kconfig_import("config.py") for _ in range(100)]
2 changes: 2 additions & 0 deletions pytest/test_simple_dep/config.import_dir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
kconfig_import("config.py")
kconfig_import("import/config.py")
2 changes: 2 additions & 0 deletions pytest/test_simple_dep/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FOO.val = True
BAR.val = True
2 changes: 2 additions & 0 deletions pytest/test_simple_dep/config1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
for _ in range(1):
FOO.val = not FOO.val
2 changes: 2 additions & 0 deletions pytest/test_simple_dep/config100.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
for _ in range(100):
FOO.val = not FOO.val
1 change: 1 addition & 0 deletions pytest/test_simple_dep/fail.config.exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1/0
1 change: 1 addition & 0 deletions pytest/test_simple_dep/fail.config.missing_dep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BAR.val = True
4 changes: 4 additions & 0 deletions pytest/test_simple_dep/fail.config.removed_dep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FOO.val = True
BAR.val = True
# Expect the following to fail since BAR is True and depends on FOO.
FOO.val = False
1 change: 1 addition & 0 deletions pytest/test_simple_dep/import/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BAR.val = False
34 changes: 34 additions & 0 deletions pytest/test_simple_dep/test_simple_dep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import os
import glob
import kconfiglib
import pytest

CUR_DIR = os.path.dirname(os.path.realpath(__file__))

@pytest.mark.parametrize("config_file",
sorted(glob.glob(CUR_DIR + '/config*.py',
recursive=True)))
def test_configs_should_not_crash(config_file):
"""Test config files that should not crash.

No asserts are used as we are just looking for exceptions.
"""
kconfig_path = CUR_DIR + '/Kconfig'

kconf = kconfiglib.Kconfig(kconfig_path)
kconf.load_config(filename=config_file)


@pytest.mark.parametrize("config_file",
sorted(glob.glob(CUR_DIR + '/fail.config*.py',
recursive=True)))
def test_configs_should_crash(config_file):
"""Test config files that should not crash.

No asserts are used as we are just looking for exceptions.
"""
kconfig_path = CUR_DIR + '/Kconfig'

kconf = kconfiglib.Kconfig(kconfig_path, warn=False)
with pytest.raises(Exception):
kconf.load_config(filename=config_file)
54 changes: 54 additions & 0 deletions pytest/test_simple_dep/test_simple_dep_bench.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import os
import glob
import kconfiglib
import pytest

CUR_DIR = os.path.dirname(os.path.realpath(__file__))


def test_bench_config(benchmark):
"""Evaluate the performance standard config file."""
kconfig_path = CUR_DIR + '/Kconfig'

kconf = kconfiglib.Kconfig(kconfig_path)
benchmark(kconf.load_config, filename=CUR_DIR + '/app.config')


def test_bench_configpy(benchmark):
"""Evaluate the performance python config."""
kconfig_path = CUR_DIR + '/Kconfig'

kconf = kconfiglib.Kconfig(kconfig_path)
benchmark(kconf.load_config, filename=CUR_DIR + '/config.py')


def test_bench_import_configpy(benchmark):
"""Evaluate the performance of importing."""
kconfig_path = CUR_DIR + '/Kconfig'

kconf = kconfiglib.Kconfig(kconfig_path)
benchmark(kconf.load_config, filename=CUR_DIR + '/config.import.py')


def test_bench_import100_configpy(benchmark):
"""Evaluate the performance of importing foo 100 times."""
kconfig_path = CUR_DIR + '/Kconfig'

kconf = kconfiglib.Kconfig(kconfig_path)
benchmark(kconf.load_config, filename=CUR_DIR + '/config.import100.py')


def test_bench_config1_configpy(benchmark):
"""Evaluate the performance of setting foo 1 time."""
kconfig_path = CUR_DIR + '/Kconfig'

kconf = kconfiglib.Kconfig(kconfig_path)
benchmark(kconf.load_config, filename=CUR_DIR + '/config1.py')


def test_bench_config100_configpy(benchmark):
"""Evaluate the performance of setting foo 100 times."""
kconfig_path = CUR_DIR + '/Kconfig'

kconf = kconfiglib.Kconfig(kconfig_path)
benchmark(kconf.load_config, filename=CUR_DIR + '/config100.py')
14 changes: 14 additions & 0 deletions pytest/test_types/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
config TEST_BOOL
bool "TEST_BOOL prompt"

config TEST_TRISTATE
tristate "TEST_TRISTATE prompt"

config TEST_STRING
string "TEST_STRING prompt"

config TEST_INT
int "TEST_INT prompt"

config TEST_HEX
hex "TEST_HEX prompt"
5 changes: 5 additions & 0 deletions pytest/test_types/config.bool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
TEST_BOOL.val = True
assert TEST_BOOL.val == True

TEST_BOOL.val = False
assert TEST_BOOL.val == False
6 changes: 6 additions & 0 deletions pytest/test_types/config.hex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TEST_HEX.val = 0x80000000
assert TEST_HEX.val == 0x80000000
TEST_HEX.val = 0
assert TEST_HEX.val == 0
TEST_HEX.val = 0x12345678
assert TEST_HEX.val == 0x12345678
10 changes: 10 additions & 0 deletions pytest/test_types/config.int.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
TEST_INT.val = -2147483648
assert TEST_INT.val == -2147483648
TEST_INT.val = -1
assert TEST_INT.val == -1
TEST_INT.val = 0
assert TEST_INT.val == 0
TEST_INT.val = 1
assert TEST_INT.val == 1
TEST_INT.val = 2147483647
assert TEST_INT.val == 2147483647
19 changes: 19 additions & 0 deletions pytest/test_types/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
TEST_BOOL.val = True
TEST_BOOL.val = False

TEST_TRISTATE.val = False
TEST_TRISTATE.val = None
TEST_TRISTATE.val = True

TEST_STRING.val = ""
TEST_STRING.val = "FOO"

TEST_INT.val = -2147483648
TEST_INT.val = -1
TEST_INT.val = 0
TEST_INT.val = 1
TEST_INT.val = 2147483647

TEST_HEX.val = 0x80000000
TEST_HEX.val = 0
TEST_HEX.val = 0x12345678
6 changes: 6 additions & 0 deletions pytest/test_types/config.string.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TEST_STRING.val = ""
assert TEST_STRING.val == ""
TEST_STRING.val = "FOO"
assert TEST_STRING.val == "FOO"
TEST_STRING.val = "FOO BAR"
assert TEST_STRING.val == "FOO BAR"
Loading