From 41edd329bd59a85db556d4299407284bd0d5c310 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 6 May 2023 17:23:57 -0500 Subject: [PATCH 1/6] Some improvements --- rcmpy/commands/apply.py | 8 ++++---- rcmpy/config/__init__.py | 4 ---- rcmpy/config/file.py | 5 +++++ rcmpy/environment/data.py | 12 ++++++++++++ rcmpy/environment/template.py | 7 +++++++ rcmpy/state/__init__.py | 1 + 6 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 rcmpy/environment/data.py diff --git a/rcmpy/commands/apply.py b/rcmpy/commands/apply.py index 46c3c63..13cb58e 100644 --- a/rcmpy/commands/apply.py +++ b/rcmpy/commands/apply.py @@ -21,6 +21,8 @@ def apply_env(args: _Namespace, env: Environment) -> int: result = 0 + is_new = env.state.is_new() + for file in env.config.files: # Check if a template is found for this file. if file.template not in env.templates_by_name: @@ -31,10 +33,8 @@ def apply_env(args: _Namespace, env: Environment) -> int: if not file.platform: continue - is_new = env.state.is_new() - # Check if this file has any updated templates. - if args.force or is_new or env.is_updated(file): + if args.force or is_new or not file.present or env.is_updated(file): template = env.templates_by_name[file.template] # If a template doesn't require rendering, use it as-is. @@ -44,7 +44,7 @@ def apply_env(args: _Namespace, env: Environment) -> int: # Render the template to the build directory. source = env.build.joinpath(file.template) with source.open("w") as path_fd: - path_fd.write(template.template.render(env.state.configs)) + path_fd.write(template.template.render(env.template_data)) env.logger.info("Rendered '%s'.", rel(source)) # Update the output file. diff --git a/rcmpy/config/__init__.py b/rcmpy/config/__init__.py index 4f1f6d8..5608447 100644 --- a/rcmpy/config/__init__.py +++ b/rcmpy/config/__init__.py @@ -54,7 +54,3 @@ def init(self, data: _JsonObject) -> None: self.templates.add(template) self.files.append(new) - - def asdict(self) -> _JsonObject: - """Obtain a dictionary representing this instance.""" - return self.data diff --git a/rcmpy/config/file.py b/rcmpy/config/file.py index 9b45147..fc26c43 100644 --- a/rcmpy/config/file.py +++ b/rcmpy/config/file.py @@ -36,6 +36,11 @@ def output(self) -> Path: """Get the full output path.""" return self.directory.joinpath(self.name) + @property + def present(self) -> bool: + """Determine if this file is currently present in the file system.""" + return self.output.is_file() + @property def platform(self) -> bool: """Determine if the platform is correct for handling this file.""" diff --git a/rcmpy/environment/data.py b/rcmpy/environment/data.py new file mode 100644 index 0000000..fdbc93a --- /dev/null +++ b/rcmpy/environment/data.py @@ -0,0 +1,12 @@ +""" +A module for exposing useful information from the current system to templates. +""" + +# built-in +from typing import Any, Dict + + +def system_data() -> Dict[str, Any]: + """Obtain useful system configuration data.""" + + return {} diff --git a/rcmpy/environment/template.py b/rcmpy/environment/template.py index e76b1d7..f061a75 100644 --- a/rcmpy/environment/template.py +++ b/rcmpy/environment/template.py @@ -3,6 +3,7 @@ """ # built-in +from copy import copy from pathlib import Path from typing import Dict, NamedTuple, Optional, Set, Tuple @@ -15,6 +16,7 @@ # internal from rcmpy.config import ManagedFile from rcmpy.environment.base import BaseEnvironment +from rcmpy.environment.data import system_data class EnvTemplate(NamedTuple): @@ -171,4 +173,9 @@ def _init_loaded(self) -> bool: self.updated_templates: Set[Path] = set() self.updated_template_names: Set[str] = set() + # Add additional information to template data. + self.template_data = copy(self.state.configs) + assert "env" not in self.template_data + self.template_data["env"] = system_data() + return result and self._init_templates(self.config.templates) diff --git a/rcmpy/state/__init__.py b/rcmpy/state/__init__.py index b015b0e..679b726 100644 --- a/rcmpy/state/__init__.py +++ b/rcmpy/state/__init__.py @@ -53,6 +53,7 @@ def init(self, data: _JsonObject) -> None: data.setdefault("previous", {}) self.previous: Dict[str, Any] = data["previous"] # type: ignore + self.previous.setdefault("variant", "default") # Variables. self.variables: Dict[str, Any] = {} From f88518246fd47dd534bc9bd323ffe10cdff9c506 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 6 May 2023 17:34:47 -0500 Subject: [PATCH 2/6] Add executable option --- rcmpy/config/__init__.py | 1 + rcmpy/config/file.py | 10 ++++++++++ rcmpy/data/schemas/ManagedFile.yaml | 5 +++++ tests/data/valid/scenarios/simple/rcmpy.yaml | 2 ++ .../valid/scenarios/simple/templates/common/test.txt | 0 .../valid/scenarios/simple/templates/test/test.txt | 0 6 files changed, 18 insertions(+) mode change 100644 => 100755 tests/data/valid/scenarios/simple/templates/common/test.txt mode change 100644 => 100755 tests/data/valid/scenarios/simple/templates/test/test.txt diff --git a/rcmpy/config/__init__.py b/rcmpy/config/__init__.py index 5608447..0db1965 100644 --- a/rcmpy/config/__init__.py +++ b/rcmpy/config/__init__.py @@ -45,6 +45,7 @@ def init(self, data: _JsonObject) -> None: Path(expandvars(file["directory"])).expanduser(), file.get("name", file["template"]), file["link"], + file["executable"], set(file.get("platforms", [])), ) diff --git a/rcmpy/config/file.py b/rcmpy/config/file.py index fc26c43..a68af4c 100644 --- a/rcmpy/config/file.py +++ b/rcmpy/config/file.py @@ -15,6 +15,12 @@ from vcorelib.paths import rel +def set_exec_flags(path: Path) -> None: + """Set the executable bits, but respect the 'read' bits.""" + mode = path.stat().st_mode + path.chmod(mode | ((mode & 0o444) >> 2)) + + @dataclass class ManagedFile: """ @@ -28,6 +34,7 @@ class ManagedFile: name: str link: bool + executable: bool platforms: Set[str] @@ -73,4 +80,7 @@ def update(self, source: Path, logger: LoggerType) -> None: else: copyfile(source, output) + if self.executable: + set_exec_flags(output) + logger.info("'%s' -> '%s'.", rel(source), rel(output)) diff --git a/rcmpy/data/schemas/ManagedFile.yaml b/rcmpy/data/schemas/ManagedFile.yaml index 37cb320..e0e09fd 100644 --- a/rcmpy/data/schemas/ManagedFile.yaml +++ b/rcmpy/data/schemas/ManagedFile.yaml @@ -17,6 +17,11 @@ properties: type: boolean default: true + # Whether or not the file should be executable. + executable: + type: boolean + default: false + name: type: string diff --git a/tests/data/valid/scenarios/simple/rcmpy.yaml b/tests/data/valid/scenarios/simple/rcmpy.yaml index 6198905..7ba9923 100644 --- a/tests/data/valid/scenarios/simple/rcmpy.yaml +++ b/tests/data/valid/scenarios/simple/rcmpy.yaml @@ -1,12 +1,14 @@ --- files: - template: test.txt + executable: true - template: dynamic.txt extra_templates: [a.txt, b.txt, c.txt] - template: basic.txt link: false + executable: true - template: linux.txt platforms: [linux] diff --git a/tests/data/valid/scenarios/simple/templates/common/test.txt b/tests/data/valid/scenarios/simple/templates/common/test.txt old mode 100644 new mode 100755 diff --git a/tests/data/valid/scenarios/simple/templates/test/test.txt b/tests/data/valid/scenarios/simple/templates/test/test.txt old mode 100644 new mode 100755 From e8fe07622368174d0ec314836330bf7238313d59 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 6 May 2023 17:57:25 -0500 Subject: [PATCH 3/6] Add useful platform info --- rcmpy/environment/data.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/rcmpy/environment/data.py b/rcmpy/environment/data.py index fdbc93a..c9c3214 100644 --- a/rcmpy/environment/data.py +++ b/rcmpy/environment/data.py @@ -3,10 +3,36 @@ """ # built-in +import platform +import sys from typing import Any, Dict def system_data() -> Dict[str, Any]: """Obtain useful system configuration data.""" - return {} + return { + "sys": { + "abiflags": sys.abiflags, + "base_exec_prefix": sys.base_exec_prefix, + "base_prefix": sys.base_prefix, + "byteorder": sys.byteorder, + "exec_prefix": sys.exec_prefix, + "executable": sys.executable, + "platform": sys.platform, + "prefix": sys.prefix, + "version": sys.version, + }, + "platform": { + "bits": platform.architecture()[0], + "linkage": platform.architecture()[1], + "machine": platform.machine(), + "node": platform.node(), + "processor": platform.processor(), + "implementation": platform.python_implementation(), + "python_version": platform.python_version(), + "release": platform.release(), + "system": platform.system(), + "version": platform.version(), + }, + } From 920b57e183f6695285fcfeeb107bc367994b74e6 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 6 May 2023 18:27:55 -0500 Subject: [PATCH 4/6] Small updates --- rcmpy/environment/data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rcmpy/environment/data.py b/rcmpy/environment/data.py index c9c3214..f8ebc39 100644 --- a/rcmpy/environment/data.py +++ b/rcmpy/environment/data.py @@ -3,17 +3,18 @@ """ # built-in +from functools import lru_cache import platform import sys from typing import Any, Dict +@lru_cache(maxsize=1) def system_data() -> Dict[str, Any]: """Obtain useful system configuration data.""" return { "sys": { - "abiflags": sys.abiflags, "base_exec_prefix": sys.base_exec_prefix, "base_prefix": sys.base_prefix, "byteorder": sys.byteorder, From 24e53cf22fd12cc136a7fec0a20b785b5c6e18ee Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 6 May 2023 22:09:25 -0500 Subject: [PATCH 5/6] Add condition to managed file --- rcmpy/commands/apply.py | 2 +- rcmpy/config/__init__.py | 1 + rcmpy/config/file.py | 10 +++++++++- rcmpy/data/schemas/ManagedFile.yaml | 6 ++++++ rcmpy/environment/template.py | 3 ++- tests/data/valid/scenarios/simple/rcmpy.yaml | 1 + 6 files changed, 20 insertions(+), 3 deletions(-) diff --git a/rcmpy/commands/apply.py b/rcmpy/commands/apply.py index 13cb58e..d16540a 100644 --- a/rcmpy/commands/apply.py +++ b/rcmpy/commands/apply.py @@ -30,7 +30,7 @@ def apply_env(args: _Namespace, env: Environment) -> int: env.logger.error("Template '%s' not found!", file.template) continue - if not file.platform: + if not file.evaluate(env.env_data): continue # Check if this file has any updated templates. diff --git a/rcmpy/config/__init__.py b/rcmpy/config/__init__.py index 0db1965..3917626 100644 --- a/rcmpy/config/__init__.py +++ b/rcmpy/config/__init__.py @@ -46,6 +46,7 @@ def init(self, data: _JsonObject) -> None: file.get("name", file["template"]), file["link"], file["executable"], + file["condition"], set(file.get("platforms", [])), ) diff --git a/rcmpy/config/file.py b/rcmpy/config/file.py index a68af4c..55ecca6 100644 --- a/rcmpy/config/file.py +++ b/rcmpy/config/file.py @@ -8,7 +8,7 @@ from pathlib import Path from shutil import copyfile import sys -from typing import Set +from typing import Any, Dict, Set # third-party from vcorelib.logging import LoggerType @@ -36,6 +36,8 @@ class ManagedFile: link: bool executable: bool + condition: str + platforms: Set[str] @property @@ -53,6 +55,12 @@ def platform(self) -> bool: """Determine if the platform is correct for handling this file.""" return not self.platforms or sys.platform in self.platforms + def evaluate(self, env: Dict[str, Any]) -> bool: + """Determine if this file should be handled.""" + return self.platform and eval( # pylint: disable=eval-used + self.condition, None, env + ) + def update_root(self, root: Path) -> None: """ If the output directory is a relative path, update it to be an diff --git a/rcmpy/data/schemas/ManagedFile.yaml b/rcmpy/data/schemas/ManagedFile.yaml index e0e09fd..da954b4 100644 --- a/rcmpy/data/schemas/ManagedFile.yaml +++ b/rcmpy/data/schemas/ManagedFile.yaml @@ -25,6 +25,12 @@ properties: name: type: string + # A string that will be evaluated for True/False and provided environment + # data. + condition: + type: string + default: "True" + platforms: type: array items: diff --git a/rcmpy/environment/template.py b/rcmpy/environment/template.py index f061a75..a6e5a13 100644 --- a/rcmpy/environment/template.py +++ b/rcmpy/environment/template.py @@ -174,8 +174,9 @@ def _init_loaded(self) -> bool: self.updated_template_names: Set[str] = set() # Add additional information to template data. + self.env_data = system_data() self.template_data = copy(self.state.configs) assert "env" not in self.template_data - self.template_data["env"] = system_data() + self.template_data["env"] = self.env_data return result and self._init_templates(self.config.templates) diff --git a/tests/data/valid/scenarios/simple/rcmpy.yaml b/tests/data/valid/scenarios/simple/rcmpy.yaml index 7ba9923..1ae5361 100644 --- a/tests/data/valid/scenarios/simple/rcmpy.yaml +++ b/tests/data/valid/scenarios/simple/rcmpy.yaml @@ -5,6 +5,7 @@ files: - template: dynamic.txt extra_templates: [a.txt, b.txt, c.txt] + condition: "'platform' in sys" - template: basic.txt link: false From 0110824a70a2e92e00652a64a9cff907da0b4946 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 6 May 2023 22:10:40 -0500 Subject: [PATCH 6/6] 1.3.0 - Bump version --- local/variables/package.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local/variables/package.yaml b/local/variables/package.yaml index 88b52bd..73241dc 100644 --- a/local/variables/package.yaml +++ b/local/variables/package.yaml @@ -1,5 +1,5 @@ --- major: 1 -minor: 2 +minor: 3 patch: 0 entry: rcmpy