Add a mechanism for building a cache of "system parameters"
- Add a system_parameters module to eryntools that implements some basic types and can write a cache to a file. - Add a system-parameters Python script that builds the cache. - Add init_system_parameters to zsh that exports an environment variable that points to the parameters file - Add an eryn.system-parameters.plist LaunchAgent file for running the script at regular intervals
This commit is contained in:
		
							parent
							
								
									05b5fef583
								
							
						
					
					
						commit
						661e5516e7
					
				
					 5 changed files with 253 additions and 0 deletions
				
			
		
							
								
								
									
										27
									
								
								LaunchAgents/eryn.system-parameters.plist
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								LaunchAgents/eryn.system-parameters.plist
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||||
|  | <plist version="1.0"> | ||||||
|  |   <dict> | ||||||
|  |     <key>Label</key> | ||||||
|  |     <string>eryn.system-parameters</string> | ||||||
|  |     <key>ProgramArguments</key> | ||||||
|  |     <array> | ||||||
|  |         <string>/Users/eryn/bin/system-parameters</string> | ||||||
|  |         <string>--output</string> | ||||||
|  |         <string>/Users/eryn/.config/eryn/system_parameters.json</string> | ||||||
|  |     </array> | ||||||
|  |     <key>WorkingDirectory</key> | ||||||
|  |     <string>/Users/eryn</string> | ||||||
|  |     <key>StandardOutPath</key> | ||||||
|  |     <string>/Users/eryn/.config/eryn/system_parameters.output.log</string> | ||||||
|  |     <key>StandardErrorPath</key> | ||||||
|  |     <string>/Users/eryn/.config/eryn/system_parameters.error.log</string> | ||||||
|  |     <key>EnvironmentVariables</key> | ||||||
|  |     <dict> | ||||||
|  |       <key>PATH</key> | ||||||
|  |       <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string> | ||||||
|  |     </dict> | ||||||
|  |     <key>RunAtLoad</key> | ||||||
|  |     <true/> | ||||||
|  |   </dict> | ||||||
|  | </plist> | ||||||
							
								
								
									
										169
									
								
								Python/eryntools/src/eryntools/dotfiles/system_parameters.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								Python/eryntools/src/eryntools/dotfiles/system_parameters.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,169 @@ | ||||||
|  | # Eryn Wells <eryn@erynwells.me> | ||||||
|  | 
 | ||||||
|  | import json | ||||||
|  | import os.path | ||||||
|  | import subprocess | ||||||
|  | import sys | ||||||
|  | from typing import Any, Optional, TextIO, Union | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ParameterValue = Union[str, int, float] | ||||||
|  | ParametersObject = dict[str, Any] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | _PARAMETERS_FILE = "~/.config/eryn/system_parameters.json" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SystemParameters: | ||||||
|  |     class _Keys: | ||||||
|  |         DATA = 'data' | ||||||
|  |         PARAMETERS = 'parameters' | ||||||
|  |         VALUE = 'value' | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         self._object: ParametersObject = {} | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def parameters(self) -> dict: | ||||||
|  |         if not self._object: | ||||||
|  |             return {} | ||||||
|  | 
 | ||||||
|  |         return self._object[SystemParameters._Keys.PARAMETERS] | ||||||
|  | 
 | ||||||
|  |     def load_from_file(self, input_file: TextIO): | ||||||
|  |         self._object = json.load(input_file) | ||||||
|  | 
 | ||||||
|  |     def write_to_file(self, file): | ||||||
|  |         json.dump(self._object, file, indent=4, cls=_JSONEncoder) | ||||||
|  | 
 | ||||||
|  |     def __dir__(self): | ||||||
|  |         return dir(self._object) | ||||||
|  | 
 | ||||||
|  |     def __getattr__(self, name: str) -> Optional[ParameterValue]: | ||||||
|  |         if not self._object: | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  |         return self._object[SystemParameters._Keys.PARAMETERS][name][SystemParameters._Keys.VALUE] | ||||||
|  | 
 | ||||||
|  |     def __setattr__(self, name: str, value: 'Parameter'): | ||||||
|  |         if not isinstance(value, Parameter): | ||||||
|  |             super().__setattr__(name, value) | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         parameter_value: ParametersObject = { | ||||||
|  |             SystemParameters._Keys.VALUE: value.value, | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         additional_data = value.additional_data | ||||||
|  |         if additional_data: | ||||||
|  |             parameter_value[SystemParameters._Keys.DATA] = additional_data | ||||||
|  | 
 | ||||||
|  |         self._object[name] = parameter_value | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Parameter: | ||||||
|  |     @property | ||||||
|  |     def value(self) -> str: | ||||||
|  |         '''The value of this parameter.''' | ||||||
|  |         raise NotImplementedError() | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def additional_data(self) -> Optional[dict[str, ParametersObject]]: | ||||||
|  |         '''Optional additional data to include in the parameter structure.''' | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CommandParameter(Parameter): | ||||||
|  |     class _Keys: | ||||||
|  |         CODE = 'code' | ||||||
|  |         COMMAND = 'command' | ||||||
|  |         RESULT = 'result' | ||||||
|  |         STDOUT = 'stdout' | ||||||
|  |         STDERR = 'stderr' | ||||||
|  | 
 | ||||||
|  |     def __init__(self, command_arguments: list): | ||||||
|  |         self.command = command_arguments | ||||||
|  |         self.result: Optional[subprocess.CompletedProcess] = None | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def value(self) -> str: | ||||||
|  |         self() | ||||||
|  |         return self.standard_output.strip() | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def standard_output(self) -> str: | ||||||
|  |         if not self.result or not self.result.stdout: | ||||||
|  |             return '' | ||||||
|  | 
 | ||||||
|  |         return self.result.stdout.decode('utf-8') | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def standard_error(self) -> str: | ||||||
|  |         if not self.result or not self.result.stderr: | ||||||
|  |             return '' | ||||||
|  | 
 | ||||||
|  |         return self.result.stderr.decode('utf-8') | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def additional_data(self) -> Optional[dict]: | ||||||
|  |         self() | ||||||
|  |         assert self.result is not None | ||||||
|  | 
 | ||||||
|  |         obj = { | ||||||
|  |             CommandParameter._Keys.COMMAND: self.result.args, | ||||||
|  |             CommandParameter._Keys.RESULT: { | ||||||
|  |                 CommandParameter._Keys.CODE: self.result.returncode, | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         stdout = self.standard_output | ||||||
|  |         if stdout: | ||||||
|  |             obj[CommandParameter._Keys.RESULT][CommandParameter._Keys.STDOUT] = stdout.strip() | ||||||
|  | 
 | ||||||
|  |         stderr = self.standard_error | ||||||
|  |         if stderr: | ||||||
|  |             obj[CommandParameter._Keys.RESULT][CommandParameter._Keys.STDERR] = stderr.strip() | ||||||
|  | 
 | ||||||
|  |         return obj | ||||||
|  | 
 | ||||||
|  |     def __call__(self) -> 'CommandParameter': | ||||||
|  |         if not self.result: | ||||||
|  |             self.result = subprocess.run( | ||||||
|  |                 self.command, | ||||||
|  |                 stdout=subprocess.PIPE, | ||||||
|  |                 stderr=subprocess.STDOUT | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         return self | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class _JSONEncoder(json.JSONEncoder): | ||||||
|  |     def default(self, o: Any) -> Any: | ||||||
|  |         if isinstance(o, SystemParameters): | ||||||
|  |             return super().default(o._object) | ||||||
|  | 
 | ||||||
|  |         return super().default(o) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main(argv) -> Optional[int]: | ||||||
|  |     import argparse | ||||||
|  | 
 | ||||||
|  |     parser = argparse.ArgumentParser(prog=argv[0]) | ||||||
|  |     parser.add_argument( | ||||||
|  |         'parameters_file', | ||||||
|  |         default=os.path.abspath(os.path.expanduser(_PARAMETERS_FILE)), | ||||||
|  |         nargs='?', | ||||||
|  |     ) | ||||||
|  |     parser.add_argument('name') | ||||||
|  |     arguments = parser.parse_args(argv[1:]) | ||||||
|  | 
 | ||||||
|  |     parameters = SystemParameters() | ||||||
|  |     with open(arguments.parameters_file) as f: | ||||||
|  |         parameters.load_from_file(f) | ||||||
|  | 
 | ||||||
|  |     print(getattr(parameters, arguments.name)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     import sys | ||||||
|  |     sys.exit(main(sys.argv) or 0) | ||||||
							
								
								
									
										41
									
								
								bin/system-parameters
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										41
									
								
								bin/system-parameters
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,41 @@ | ||||||
|  | #!/usr/bin/env python3 | ||||||
|  | 
 | ||||||
|  | import argparse | ||||||
|  | from eryntools.dotfiles.system_parameters import CommandParameter, SystemParameters | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class HomebrewPrefix(CommandParameter): | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__(['brew', '--prefix']) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class XcodeSelectPrintPath(CommandParameter): | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__(['xcode-select', '-p']) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main(argv): | ||||||
|  |     parser = argparse.ArgumentParser(prog=argv[0]) | ||||||
|  |     parser.add_argument('-f', '--file', type=argparse.FileType('r')) | ||||||
|  |     parser.add_argument('-o', '--output', type=argparse.FileType('w'), default=sys.stdout) | ||||||
|  |     arguments = parser.parse_args(argv[1:]) | ||||||
|  | 
 | ||||||
|  |     output_parameters = SystemParameters() | ||||||
|  | 
 | ||||||
|  |     if arguments.file: | ||||||
|  |         output_parameters.load_from_file(arguments.file) | ||||||
|  | 
 | ||||||
|  |     parameters = { | ||||||
|  |         'homebrew_prefix': HomebrewPrefix(), | ||||||
|  |         'xcode_path': XcodeSelectPrintPath(), | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for name, parameter in parameters.items(): | ||||||
|  |         setattr(output_parameters, name, parameter) | ||||||
|  | 
 | ||||||
|  |     output_parameters.write_to_file(arguments.output) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     import sys | ||||||
|  |     sys.exit(main(sys.argv) or 0) | ||||||
							
								
								
									
										15
									
								
								zsh/func/init_system_parameters
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								zsh/func/init_system_parameters
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | #!/usr/bin/env zsh | ||||||
|  | # Eryn Wells <eryn@erynwells.me> | ||||||
|  | 
 | ||||||
|  | function init_system_parameters | ||||||
|  | { | ||||||
|  |     local PARAMETERS_FILE="$HOME/.config/eryn/system_parameters" | ||||||
|  | 
 | ||||||
|  |     if [[ ! -e "$PARAMETERS_FILE" ]]; then | ||||||
|  |         return | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     export SYSTEM_PARAMETERS_FILE="$PARAMETERS_FILE" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | init_system_parameters "$@" | ||||||
							
								
								
									
										1
									
								
								zshenv
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								zshenv
									
										
									
									
									
								
							|  | @ -22,6 +22,7 @@ autoload -Uz do_init_functions | ||||||
| 
 | 
 | ||||||
| typeset -a zsh_init_env_functions=( \ | typeset -a zsh_init_env_functions=( \ | ||||||
|     init_path \ |     init_path \ | ||||||
|  |     init_system_parameters \ | ||||||
|     init_env \ |     init_env \ | ||||||
|     init_env_aliases \ |     init_env_aliases \ | ||||||
|     init_env_python \ |     init_env_python \ | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue