idf-build-apps 2.5.0rc1__py3-none-any.whl → 2.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -119,3 +119,4 @@ fish:
119
119
  # Not required to be in the config file, only run once
120
120
  register-python-argcomplete --shell fish idf-build-apps >~/.config/fish/completions/idf-build-apps.fish
121
121
  """
122
+ IDF_BUILD_APPS_TOML_FN = '.idf_build_apps.toml'
idf_build_apps/finder.py CHANGED
@@ -31,7 +31,7 @@ def _get_apps_from_path(
31
31
  target: str,
32
32
  *,
33
33
  app_cls: t.Type[App] = CMakeApp,
34
- args: FindArguments = FindArguments(),
34
+ args: FindArguments,
35
35
  ) -> t.List[App]:
36
36
  def _validate_app(_app: App) -> bool:
37
37
  if target not in _app.supported_targets:
@@ -133,7 +133,7 @@ def _find_apps(
133
133
  target: str,
134
134
  *,
135
135
  app_cls: t.Type[App] = CMakeApp,
136
- args: FindArguments = FindArguments(),
136
+ args: FindArguments,
137
137
  ) -> t.List[App]:
138
138
  LOGGER.debug(
139
139
  'Looking for %s apps in %s%s with target %s',
idf_build_apps/main.py CHANGED
@@ -10,7 +10,6 @@ import os
10
10
  import sys
11
11
  import textwrap
12
12
  import typing as t
13
- from dataclasses import asdict
14
13
 
15
14
  import argcomplete
16
15
  from pydantic import (
@@ -18,7 +17,13 @@ from pydantic import (
18
17
  create_model,
19
18
  )
20
19
 
21
- from idf_build_apps.args import BuildArguments, DumpManifestShaArguments, FindArguments, add_arguments_to_parser
20
+ from idf_build_apps.args import (
21
+ BuildArguments,
22
+ DumpManifestShaArguments,
23
+ FindArguments,
24
+ add_args_to_parser,
25
+ apply_config_file,
26
+ )
22
27
 
23
28
  from .app import (
24
29
  App,
@@ -55,6 +60,7 @@ def find_apps(
55
60
  target: t.Optional[str] = None,
56
61
  *,
57
62
  find_arguments: t.Optional[FindArguments] = None,
63
+ config_file: t.Optional[str] = None,
58
64
  **kwargs,
59
65
  ) -> t.List[App]:
60
66
  """
@@ -62,6 +68,17 @@ def find_apps(
62
68
 
63
69
  :return: list of found apps
64
70
  """
71
+ apply_config_file(config_file)
72
+
73
+ # compatible with old usage
74
+ ## `preserve`
75
+ if 'preserve' in kwargs:
76
+ LOGGER.warning(
77
+ 'Passing "preserve" directly is deprecated. '
78
+ 'Pass "no_preserve" instead to disable preserving the build directory'
79
+ )
80
+ kwargs['no_preserve'] = not kwargs.pop('preserve')
81
+
65
82
  if find_arguments is None:
66
83
  find_arguments = FindArguments(
67
84
  paths=to_list(paths), # type: ignore
@@ -104,21 +121,40 @@ def find_apps(
104
121
 
105
122
 
106
123
  def build_apps(
107
- apps: t.Union[t.List[App], App, None] = None, *, build_arguments: t.Optional[BuildArguments] = None, **kwargs
124
+ apps: t.Union[t.List[App], App, None] = None,
125
+ *,
126
+ build_arguments: t.Optional[BuildArguments] = None,
127
+ config_file: t.Optional[str] = None,
128
+ **kwargs,
108
129
  ) -> int:
109
130
  """
110
131
  Build all the specified apps. For all kwargs, please refer to `BuildArguments`
111
132
 
112
133
  :return: exit code
113
134
  """
114
- apps = to_list(apps)
135
+ apply_config_file(config_file)
136
+
137
+ # compatible with old usage
138
+ ## `check_app_dependencies`
139
+ if 'check_app_dependencies' in kwargs:
140
+ LOGGER.warning(
141
+ 'Passing "check_app_dependencies" directly is deprecated. '
142
+ 'Pass "modified_components" instead to enable dependency-driven build feature'
143
+ )
144
+ kwargs.pop('check_app_dependencies')
145
+
115
146
  if build_arguments is None:
116
147
  build_arguments = BuildArguments(
117
148
  **kwargs,
118
149
  )
119
150
 
151
+ apps = to_list(apps)
120
152
  if apps is None:
121
- apps = find_apps(find_arguments=FindArguments.from_dict(asdict(build_arguments)))
153
+ apps = find_apps(
154
+ find_arguments=FindArguments(
155
+ **{k: v for k, v in build_arguments.model_dump().items() if k in FindArguments.model_fields}
156
+ )
157
+ )
122
158
 
123
159
  test_suite = TestSuite('build_apps')
124
160
 
@@ -255,6 +291,9 @@ def get_parser() -> argparse.ArgumentParser:
255
291
  )
256
292
  actions = parser.add_subparsers(dest='action', required=True)
257
293
 
294
+ common_args = argparse.ArgumentParser(add_help=False)
295
+ common_args.add_argument('--config-file', help='Path to the config file')
296
+
258
297
  ########
259
298
  # Find #
260
299
  ########
@@ -266,8 +305,9 @@ def get_parser() -> argparse.ArgumentParser:
266
305
  'By default, print the found apps in stdout. '
267
306
  'To find apps for all chips use the `--target` option with the `all` argument.',
268
307
  formatter_class=IdfBuildAppsCliFormatter,
308
+ parents=[common_args],
269
309
  )
270
- add_arguments_to_parser(FindArguments, find_parser)
310
+ add_args_to_parser(FindArguments, find_parser)
271
311
 
272
312
  #########
273
313
  # Build #
@@ -278,8 +318,9 @@ def get_parser() -> argparse.ArgumentParser:
278
318
  description='Build the application in the given path or paths for specified chips. '
279
319
  '`--path` and `--target` options must be provided.',
280
320
  formatter_class=IdfBuildAppsCliFormatter,
321
+ parents=[common_args],
281
322
  )
282
- add_arguments_to_parser(BuildArguments, build_parser)
323
+ add_args_to_parser(BuildArguments, build_parser)
283
324
 
284
325
  ###############
285
326
  # Completions #
@@ -314,8 +355,9 @@ def get_parser() -> argparse.ArgumentParser:
314
355
  'dump-manifest-sha',
315
356
  help='Dump the manifest files SHA values. '
316
357
  'This could be useful in CI to check if the manifest files are changed.',
358
+ parents=[common_args],
317
359
  )
318
- add_arguments_to_parser(DumpManifestShaArguments, dump_manifest_parser)
360
+ add_args_to_parser(DumpManifestShaArguments, dump_manifest_parser)
319
361
 
320
362
  return parser
321
363
 
@@ -340,15 +382,20 @@ def main():
340
382
  handle_completions(args)
341
383
  sys.exit(0)
342
384
 
343
- if args.action == 'dump-manifest-sha':
344
- arguments = DumpManifestShaArguments.from_dict(drop_none_kwargs(vars(args)))
385
+ kwargs = vars(args)
386
+ action = kwargs.pop('action')
387
+ config_file = kwargs.pop('config_file')
388
+
389
+ apply_config_file(config_file)
390
+
391
+ if action == 'dump-manifest-sha':
392
+ arguments = DumpManifestShaArguments(**drop_none_kwargs(kwargs))
345
393
  Manifest.from_files(arguments.manifest_files).dump_sha_values(arguments.output)
346
394
  sys.exit(0)
347
-
348
- if args.action == 'find':
349
- arguments = FindArguments.from_dict(drop_none_kwargs(vars(args)))
395
+ elif action == 'find':
396
+ arguments = FindArguments(**drop_none_kwargs(kwargs))
350
397
  else:
351
- arguments = BuildArguments.from_dict(drop_none_kwargs(vars(args)))
398
+ arguments = BuildArguments(**drop_none_kwargs(kwargs))
352
399
 
353
400
  # real call starts here
354
401
  # build also needs to find first
@@ -395,7 +442,8 @@ def main():
395
442
  for app in failed_apps:
396
443
  print(f' {app}')
397
444
 
398
- sys.exit(ret_code)
445
+ if ret_code != 0:
446
+ sys.exit(ret_code)
399
447
 
400
448
 
401
449
  def json_to_app(json_str: str, extra_classes: t.Optional[t.List[t.Type[App]]] = None) -> App:
@@ -1,6 +1,5 @@
1
1
  # SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2
2
  # SPDX-License-Identifier: Apache-2.0
3
-
4
3
  import operator
5
4
  import os
6
5
  from ast import (
@@ -16,6 +15,7 @@ from packaging.version import (
16
15
  from pyparsing import (
17
16
  Keyword,
18
17
  Literal,
18
+ MatchFirst,
19
19
  ParseResults,
20
20
  QuotedString,
21
21
  Suppress,
@@ -35,6 +35,7 @@ from ..constants import (
35
35
  IDF_VERSION_PATCH,
36
36
  )
37
37
  from ..utils import (
38
+ InvalidIfClause,
38
39
  InvalidInput,
39
40
  to_version,
40
41
  )
@@ -177,22 +178,31 @@ class BoolExpr(Stmt):
177
178
  pass
178
179
 
179
180
 
180
- class BoolAnd(BoolExpr):
181
- def __init__(self, t: ParseResults):
182
- self.left: BoolStmt = t[0][0]
183
- self.right: BoolStmt = t[0][2]
181
+ def _and(_l, _r):
182
+ return _l and _r
184
183
 
185
- def get_value(self, target: str, config_name: str) -> Any:
186
- return self.left.get_value(target, config_name) and self.right.get_value(target, config_name)
184
+
185
+ def _or(_l, _r):
186
+ return _l or _r
187
187
 
188
188
 
189
- class BoolOr(BoolExpr):
189
+ class BoolOrAnd(BoolExpr):
190
190
  def __init__(self, t: ParseResults):
191
+ if len(t[0]) > 3:
192
+ raise InvalidIfClause(
193
+ 'Chaining "and"/"or" is not allowed. Please use paratheses instead. '
194
+ 'For example: "a and b and c" should be "(a and b) and c".'
195
+ )
191
196
  self.left: BoolStmt = t[0][0]
192
197
  self.right: BoolStmt = t[0][2]
193
198
 
199
+ if t[0][1] == 'and':
200
+ self.operation = _and
201
+ if t[0][1] == 'or':
202
+ self.operation = _or
203
+
194
204
  def get_value(self, target: str, config_name: str) -> Any:
195
- return self.left.get_value(target, config_name) or self.right.get_value(target, config_name)
205
+ return self.operation(self.left.get_value(target, config_name), self.right.get_value(target, config_name))
196
206
 
197
207
 
198
208
  CAP_WORD = Word(alphas.upper(), nums + alphas.upper() + '_').setParseAction(ChipAttr)
@@ -225,7 +235,6 @@ OR = Keyword('or')
225
235
  BOOL_EXPR = infixNotation(
226
236
  BOOL_STMT,
227
237
  [
228
- (AND, 2, opAssoc.LEFT, BoolAnd),
229
- (OR, 2, opAssoc.LEFT, BoolOr),
238
+ (MatchFirst((AND, OR)), 2, opAssoc.LEFT, BoolOrAnd),
230
239
  ],
231
240
  )
@@ -17,6 +17,7 @@ from ..constants import (
17
17
  from ..utils import (
18
18
  InvalidIfClause,
19
19
  InvalidManifest,
20
+ PathLike,
20
21
  to_absolute_path,
21
22
  )
22
23
  from ..yaml import (
@@ -34,14 +35,21 @@ class IfClause:
34
35
  def __init__(self, stmt: str, temporary: bool = False, reason: t.Optional[str] = None) -> None:
35
36
  try:
36
37
  self.stmt: BoolStmt = BOOL_EXPR.parseString(stmt)[0]
37
- except ParseException:
38
- raise InvalidIfClause(f'Invalid if statement: {stmt}')
38
+ except (ParseException, InvalidIfClause) as ex:
39
+ raise InvalidIfClause(f'Invalid if clause: {stmt}. {ex}')
39
40
 
40
41
  self.temporary = temporary
41
42
  self.reason = reason
42
43
 
43
44
  if self.temporary is True and not self.reason:
44
- raise InvalidIfClause('"reason" must be set when "temporary: true"')
45
+ raise InvalidIfClause(
46
+ f'Invalid if clause "{stmt}". '
47
+ f'"reason" must be set when "temporary: true". '
48
+ f'For example:\n'
49
+ f' - if: {stmt}\n'
50
+ f' temporary: true\n'
51
+ f' reason: lack of ci runners'
52
+ )
45
53
 
46
54
  def get_value(self, target: str, config_name: str) -> t.Any:
47
55
  return self.stmt.get_value(target, config_name)
@@ -145,7 +153,7 @@ class FolderRule:
145
153
  self.depends_components,
146
154
  self.depends_filepatterns,
147
155
  ]:
148
- sha.update(pickle.dumps(obj))
156
+ sha.update(pickle.dumps(obj, protocol=4)) # protocol 4 by default is set in Python 3.8
149
157
 
150
158
  return sha.hexdigest()
151
159
 
@@ -235,7 +243,7 @@ class Manifest:
235
243
  self._root_path = to_absolute_path(root_path)
236
244
 
237
245
  @classmethod
238
- def from_files(cls, paths: t.Iterable[str], *, root_path: str = os.curdir) -> 'Manifest':
246
+ def from_files(cls, paths: t.Iterable[PathLike], *, root_path: str = os.curdir) -> 'Manifest':
239
247
  """
240
248
  Create a Manifest instance from multiple manifest files
241
249
 
@@ -244,10 +252,11 @@ class Manifest:
244
252
  :return: Manifest instance
245
253
  """
246
254
  # folder, defined as dict
247
- _known_folders: t.Dict[str, str] = dict()
255
+ _known_folders: t.Dict[str, PathLike] = dict()
248
256
 
249
257
  rules: t.List[FolderRule] = []
250
258
  for path in paths:
259
+ LOGGER.debug('Loading manifest file %s', path)
251
260
  _manifest = cls.from_file(path, root_path=root_path)
252
261
 
253
262
  for rule in _manifest.rules:
@@ -265,7 +274,7 @@ class Manifest:
265
274
  return Manifest(rules, root_path=root_path)
266
275
 
267
276
  @classmethod
268
- def from_file(cls, path: str, *, root_path: str = os.curdir) -> 'Manifest':
277
+ def from_file(cls, path: PathLike, *, root_path: str = os.curdir) -> 'Manifest':
269
278
  """
270
279
  Create a Manifest instance from a manifest file
271
280
 
@@ -343,11 +352,14 @@ class Manifest:
343
352
  return x
344
353
 
345
354
  for folder, sha_value in recorded__rel_folder__sha__dict.items():
355
+ # removed
346
356
  if folder not in self__rel_folder__sha__dict:
347
357
  diff_folders.add(_path(folder))
358
+ # modified
348
359
  elif sha_value != self__rel_folder__sha__dict[folder]:
349
360
  diff_folders.add(_path(folder))
350
361
 
362
+ # new
351
363
  for folder in self__rel_folder__sha__dict:
352
364
  if folder not in recorded__rel_folder__sha__dict:
353
365
  diff_folders.add(_path(folder))
idf_build_apps/utils.py CHANGED
@@ -13,6 +13,7 @@ import typing as t
13
13
  from copy import (
14
14
  deepcopy,
15
15
  )
16
+ from pathlib import Path
16
17
 
17
18
  from packaging.version import (
18
19
  Version,
@@ -124,7 +125,7 @@ class InvalidManifest(SystemExit):
124
125
  """Invalid manifest file"""
125
126
 
126
127
 
127
- def rmdir(path: str, exclude_file_patterns: t.Union[t.List[str], str, None] = None) -> None:
128
+ def rmdir(path: t.Union[Path, str], exclude_file_patterns: t.Union[t.List[str], str, None] = None) -> None:
128
129
  if not exclude_file_patterns:
129
130
  shutil.rmtree(path, ignore_errors=True)
130
131
  return
@@ -387,3 +388,6 @@ class BaseModel(_BaseModel):
387
388
 
388
389
  def drop_none_kwargs(d: dict) -> dict:
389
390
  return {k: v for k, v in d.items() if v is not None}
391
+
392
+
393
+ PathLike = t.Union[str, Path]
File without changes
@@ -0,0 +1,120 @@
1
+ # SPDX-FileCopyrightText: 2022 Samuel Colvin and other contributors
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ Partially copied from https://github.com/pydantic/pydantic-settings v2.5.2
6
+ since python 3.7 version got dropped at pydantic-settings 2.1.0
7
+ but the feature we need introduced in 2.2.0
8
+
9
+ For contributing history please refer to the original github page
10
+ For the full license text refer to
11
+ https://github.com/pydantic/pydantic-settings/blob/9b73e924cab136d876907af0c6836dcca09ac35c/LICENSE
12
+
13
+ Modifications:
14
+ - use toml instead of tomli when python < 3.11
15
+ - stop using global variables
16
+ - fix some warnings
17
+ """
18
+
19
+ import os
20
+ import sys
21
+ from abc import ABC, abstractmethod
22
+ from pathlib import Path
23
+ from typing import Any, Dict, List, Optional, Tuple, Type, Union
24
+
25
+ from pydantic_settings import InitSettingsSource
26
+ from pydantic_settings.main import BaseSettings
27
+
28
+ PathType = Union[Path, str, List[Union[Path, str]], Tuple[Union[Path, str], ...]]
29
+ DEFAULT_PATH: PathType = Path('')
30
+
31
+
32
+ class ConfigFileSourceMixin(ABC):
33
+ def _read_files(self, files: Optional[PathType]) -> Dict[str, Any]:
34
+ if files is None:
35
+ return {}
36
+ if isinstance(files, (str, os.PathLike)):
37
+ files = [files]
38
+ kwargs: Dict[str, Any] = {}
39
+ for file in files:
40
+ file_path = Path(file).expanduser()
41
+ if file_path.is_file():
42
+ kwargs.update(self._read_file(file_path))
43
+ return kwargs
44
+
45
+ @abstractmethod
46
+ def _read_file(self, path: Path) -> Dict[str, Any]:
47
+ pass
48
+
49
+
50
+ class TomlConfigSettingsSource(InitSettingsSource, ConfigFileSourceMixin):
51
+ """
52
+ A source class that loads variables from a TOML file
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ settings_cls: Type[BaseSettings],
58
+ toml_file: Optional[PathType] = DEFAULT_PATH,
59
+ ):
60
+ self.toml_file_path = toml_file if toml_file != DEFAULT_PATH else settings_cls.model_config.get('toml_file')
61
+ self.toml_data = self._read_files(self.toml_file_path)
62
+ super().__init__(settings_cls, self.toml_data)
63
+
64
+ def _read_file(self, file_path: Path) -> Dict[str, Any]:
65
+ if sys.version_info < (3, 11):
66
+ import toml
67
+
68
+ with open(file_path) as toml_file:
69
+ return toml.load(toml_file)
70
+ else:
71
+ import tomllib
72
+
73
+ with open(file_path, 'rb') as toml_file:
74
+ return tomllib.load(toml_file)
75
+
76
+
77
+ class PyprojectTomlConfigSettingsSource(TomlConfigSettingsSource):
78
+ """
79
+ A source class that loads variables from a `pyproject.toml` file.
80
+ """
81
+
82
+ def __init__(
83
+ self,
84
+ settings_cls: Type[BaseSettings],
85
+ toml_file: Optional[Path] = None,
86
+ ) -> None:
87
+ self.toml_file_path = self._pick_pyproject_toml_file(
88
+ toml_file, settings_cls.model_config.get('pyproject_toml_depth', 0)
89
+ )
90
+ self.toml_table_header: Tuple[str, ...] = settings_cls.model_config.get(
91
+ 'pyproject_toml_table_header', ('tool', 'pydantic-settings')
92
+ )
93
+ self.toml_data = self._read_files(self.toml_file_path)
94
+ for key in self.toml_table_header:
95
+ self.toml_data = self.toml_data.get(key, {})
96
+ super(TomlConfigSettingsSource, self).__init__(settings_cls, self.toml_data)
97
+
98
+ @staticmethod
99
+ def _pick_pyproject_toml_file(provided: Optional[Path], depth: int) -> Path:
100
+ """Pick a `pyproject.toml` file path to use.
101
+
102
+ Args:
103
+ provided: Explicit path provided when instantiating this class.
104
+ depth: Number of directories up the tree to check of a pyproject.toml.
105
+
106
+ """
107
+ if provided:
108
+ return provided.resolve()
109
+ rv = Path.cwd() / 'pyproject.toml'
110
+ count = 0
111
+ if not rv.is_file():
112
+ child = rv.parent.parent / 'pyproject.toml'
113
+ while count < depth:
114
+ if child.is_file():
115
+ return child
116
+ if str(child.parent) == rv.root:
117
+ break # end discovery after checking system root once
118
+ child = child.parent.parent / 'pyproject.toml'
119
+ count += 1
120
+ return rv
@@ -5,6 +5,8 @@ import typing as t
5
5
 
6
6
  import yaml
7
7
 
8
+ from ..utils import PathLike
9
+
8
10
 
9
11
  def parse_postfixes(manifest_dict: t.Dict):
10
12
  for folder, folder_rule in manifest_dict.items():
@@ -60,7 +62,7 @@ def parse_postfixes(manifest_dict: t.Dict):
60
62
  manifest_dict[folder] = updated_folder
61
63
 
62
64
 
63
- def parse(path: str) -> t.Dict:
65
+ def parse(path: PathLike) -> t.Dict:
64
66
  with open(path) as f:
65
67
  manifest_dict = yaml.safe_load(f) or {}
66
68
  parse_postfixes(manifest_dict)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: idf-build-apps
3
- Version: 2.5.0rc1
3
+ Version: 2.5.1
4
4
  Summary: Tools for building ESP-IDF related apps.
5
5
  Author-email: Fu Hanxi <fuhanxi@espressif.com>
6
6
  Requires-Python: >=3.7
@@ -18,6 +18,7 @@ Requires-Dist: pyyaml
18
18
  Requires-Dist: packaging
19
19
  Requires-Dist: toml; python_version < '3.11'
20
20
  Requires-Dist: pydantic~=2.0
21
+ Requires-Dist: pydantic_settings
21
22
  Requires-Dist: argcomplete>=3
22
23
  Requires-Dist: typing-extensions; python_version < '3.11'
23
24
  Requires-Dist: sphinx ; extra == "doc"
@@ -0,0 +1,27 @@
1
+ idf_build_apps/__init__.py,sha256=hPsqVwLnkcGIEQNAReIvWhXRDoov8t70997wSn1zJ0g,650
2
+ idf_build_apps/__main__.py,sha256=8E-5xHm2MlRun0L88XJleNh5U50dpE0Q1nK5KqomA7I,182
3
+ idf_build_apps/app.py,sha256=F-MKOsaz7cJ0H2wsEE4gpO4kkkEdkyFmIZBHDoM2qgs,37359
4
+ idf_build_apps/args.py,sha256=Y4Wwoi8XYLjf8lWmCUm197NSzSBa4eC95QAyx4q7-6Q,31668
5
+ idf_build_apps/autocompletions.py,sha256=g-bx0pzXoFKI0VQqftkHyGVWN6MLjuFOdozeuAf45yo,2138
6
+ idf_build_apps/constants.py,sha256=07ve2FtWuLBuc_6LFzbs1XncB1VNi9HJUqGjQQauRNM,3952
7
+ idf_build_apps/finder.py,sha256=kfZaGWJfPUwWAbaOj_W3Fu97SIIFEsv1R_dJucjbFHw,5691
8
+ idf_build_apps/log.py,sha256=pyvT7N4MWzGjIXph5mThQCGBiSt53RNPW0WrFfLr0Kw,2650
9
+ idf_build_apps/main.py,sha256=Z_hetbOavgCJZQPaP01_jx57fR1w0DefiFz0J2cCwp0,16498
10
+ idf_build_apps/session_args.py,sha256=2WDTy40IFAc0KQ57HaeBcYj_k10eUXRKkDOWLrFCaHY,2985
11
+ idf_build_apps/utils.py,sha256=s4D8P7QA17XcaCUQ_EoiNOW_VpU3cPQgiZVV9KQ8I30,10171
12
+ idf_build_apps/junit/__init__.py,sha256=IxvdaS6eSXp7kZxRuXqyZyGxuA_A1nOW1jF1HMi8Gns,231
13
+ idf_build_apps/junit/report.py,sha256=T7dVU3Sz5tqjfbcFW7wjsb65PDH6C2HFf73ePJqBhMs,6555
14
+ idf_build_apps/junit/utils.py,sha256=j0PYhFTZjXtTwkENdeL4bFJcX24ktf1CsOOVXz65yNo,1297
15
+ idf_build_apps/manifest/__init__.py,sha256=Q2-cb3ngNjnl6_zWhUfzZZB10f_-Rv2JYNck3Lk7UkQ,133
16
+ idf_build_apps/manifest/if_parser.py,sha256=AAEyYPgcBWL2ToCmcvhx3fl8ebYdC5-LMvvDkYtWn8o,6546
17
+ idf_build_apps/manifest/manifest.py,sha256=6F6Nt93eaoBOHlGHZtv2hHT8Zw0HmJf0d_MESlgye6M,14478
18
+ idf_build_apps/manifest/soc_header.py,sha256=PzJ37xFspt5f0AXWvAFNA_avHZA9fMXHBrwDYLi3qEI,4344
19
+ idf_build_apps/vendors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ idf_build_apps/vendors/pydantic_sources.py,sha256=2IN6opo6qjCwaqhERFbgA4PtEwqKaTtEkccy5_fYAT0,4130
21
+ idf_build_apps/yaml/__init__.py,sha256=W-3z5no07RQ6eYKGyOAPA8Z2CLiMPob8DD91I4URjrA,162
22
+ idf_build_apps/yaml/parser.py,sha256=b3LvogO6do-eJPRsYzT-8xk8AT2MnXpLCzQutJqyC7M,2128
23
+ idf_build_apps-2.5.1.dist-info/entry_points.txt,sha256=3pVUirUEsb6jsDRikkQWNUt4hqLK2ci1HvW_Vf8b6uE,59
24
+ idf_build_apps-2.5.1.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
25
+ idf_build_apps-2.5.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
26
+ idf_build_apps-2.5.1.dist-info/METADATA,sha256=lvliMyMStV-5w_77UputZgbH9ZL2aDagexjxU6OUi-I,4610
27
+ idf_build_apps-2.5.1.dist-info/RECORD,,
idf_build_apps/config.py DELETED
@@ -1,108 +0,0 @@
1
- # SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
2
- # SPDX-License-Identifier: Apache-2.0
3
-
4
- import os
5
- import typing as t
6
- from pathlib import (
7
- Path,
8
- )
9
-
10
- from .utils import (
11
- to_absolute_path,
12
- )
13
-
14
-
15
- class InvalidTomlError(SystemExit):
16
- def __init__(self, filepath: t.Union[str, Path], msg: str) -> None:
17
- super().__init__(f'Failed parsing toml file "{filepath}" with error: {msg}')
18
-
19
-
20
- PYPROJECT_TOML_FN = 'pyproject.toml'
21
- IDF_BUILD_APPS_TOML_FN = '.idf_build_apps.toml'
22
-
23
-
24
- def load_toml(filepath: t.Union[str, Path]) -> dict:
25
- try:
26
- import tomllib # type: ignore # python 3.11
27
-
28
- try:
29
- with open(str(filepath), 'rb') as fr:
30
- return tomllib.load(fr)
31
- except Exception as e:
32
- raise InvalidTomlError(filepath, str(e))
33
- except ImportError:
34
- import toml
35
-
36
- try:
37
- return toml.load(str(filepath))
38
- except Exception as e:
39
- raise InvalidTomlError(filepath, str(e))
40
-
41
-
42
- def _get_config_from_file(filepath: str) -> t.Tuple[t.Optional[dict], str]:
43
- config = None
44
- if os.path.isfile(filepath):
45
- if os.path.basename(filepath) == PYPROJECT_TOML_FN:
46
- tool = load_toml(filepath).get('tool', None)
47
- if tool:
48
- config = tool.get('idf-build-apps', None)
49
- elif config is None:
50
- config = load_toml(filepath)
51
-
52
- return config, filepath
53
-
54
-
55
- def _get_config_from_path(dirpath: str) -> t.Tuple[t.Optional[dict], str]:
56
- config = None
57
- filepath = dirpath
58
- if os.path.isfile(os.path.join(dirpath, PYPROJECT_TOML_FN)):
59
- config, filepath = _get_config_from_file(os.path.join(dirpath, PYPROJECT_TOML_FN))
60
-
61
- if config is None and os.path.isfile(os.path.join(dirpath, IDF_BUILD_APPS_TOML_FN)):
62
- config, filepath = _get_config_from_file(os.path.join(dirpath, IDF_BUILD_APPS_TOML_FN))
63
-
64
- return config, filepath
65
-
66
-
67
- def get_valid_config(starts_from: str = os.getcwd(), custom_path: t.Optional[str] = None) -> t.Optional[dict]:
68
- """
69
- Get the valid config from the current directory or its parent directories.
70
-
71
- If the custom path is provided, it will be used to get the config file.
72
-
73
- Otherwise, the search will start from the current working directory and go up to the root directory.
74
-
75
- The search will stop in the following cases:
76
-
77
- - A valid config file is found.
78
- - A `.git` directory is found.
79
- - The root directory is reached.
80
-
81
- :param starts_from: starting directory to search for the config file. Default is the current working directory.
82
- :param custom_path: custom path to the config file.
83
- :return: the valid config dict if found, otherwise None.
84
- """
85
- root_dir = to_absolute_path('/')
86
- cur_dir = to_absolute_path(starts_from)
87
-
88
- config = None
89
- if custom_path and os.path.isfile(custom_path):
90
- config, filepath = _get_config_from_file(to_absolute_path(custom_path))
91
- if config is not None:
92
- # use print here since the verbose settings may be set in the config file
93
- print(f'Using custom config file: {filepath}')
94
- return config
95
-
96
- while cur_dir != root_dir and config is None:
97
- config, filepath = _get_config_from_path(cur_dir)
98
- if config is not None:
99
- # use print here since the verbose settings may be set in the config file
100
- print(f'Using config file: {filepath}')
101
- return config
102
-
103
- if os.path.exists(os.path.join(cur_dir, '.git')):
104
- break
105
-
106
- cur_dir = os.path.abspath(os.path.join(cur_dir, '..'))
107
-
108
- return None