Skip to content

Commit

Permalink
Merge pull request #13 from vkottler/dev/1.1.0
Browse files Browse the repository at this point in the history
1.1.0 - Add a few features and improvements
  • Loading branch information
vkottler authored Apr 20, 2023
2 parents ccaea13 + 65dee92 commit defe9b1
Show file tree
Hide file tree
Showing 17 changed files with 190 additions and 96 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ htmlcov
*-stubs
coverage*.xml
tags
*-output
*-out*
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[DESIGN]
max-attributes=8
max-attributes=10
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.2
hash=4e253e82b70bf6a2a6479e0620da720b
hash=d6cd0e307dd25044b7eb3a7adaf13b33
=====================================
-->

# rcmpy ([1.0.0](https://pypi.org/project/rcmpy/))
# rcmpy ([1.1.0](https://pypi.org/project/rcmpy/))

[![python](https://img.shields.io/pypi/pyversions/rcmpy.svg)](https://pypi.org/project/rcmpy/)
![Build Status](https://github.com/vkottler/rcmpy/workflows/Python%20Package/badge.svg)
Expand Down Expand Up @@ -149,10 +149,12 @@ commands:
```
$ ./venv3.8/bin/rcmpy apply -h
usage: rcmpy apply [-h]
usage: rcmpy apply [-h] [-f] [-d]
optional arguments:
-h, --help show this help message and exit
-h, --help show this help message and exit
-f, --force whether or not to forcibly render all outputs
-d, --dry-run whether or not to update output files
```

Expand Down
2 changes: 1 addition & 1 deletion local/configs/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: A configuration-file management system.
entry: {{entry}}
requirements:
- datazen
- vcorelib>=1.5.2
- vcorelib>=1.6.0
dev_requirements:
- setuptools-wrapper
- types-setuptools
Expand Down
2 changes: 1 addition & 1 deletion local/variables/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
major: 1
minor: 0
minor: 1
patch: 0
entry: rcmpy
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"

[project]
name = "rcmpy"
version = "1.0.0"
version = "1.1.0"
description = "A configuration-file management system."
readme = "README.md"
requires-python = ">=3.7"
Expand Down
4 changes: 2 additions & 2 deletions rcmpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.2
# hash=de6d52cb7b3c576d64df05d995c59d25
# hash=db6b75d059437ccfb73620d52efc9103
# =====================================

"""
Expand All @@ -10,4 +10,4 @@

DESCRIPTION = "A configuration-file management system."
PKG_NAME = "rcmpy"
VERSION = "1.0.0"
VERSION = "1.1.0"
28 changes: 21 additions & 7 deletions rcmpy/commands/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from rcmpy.environment import Environment, load_environment


def apply_env(env: Environment) -> int:
def apply_env(args: _Namespace, env: Environment) -> int:
"""Apply pending changes from the environment."""

result = 0
Expand All @@ -31,7 +31,7 @@ def apply_env(env: Environment) -> int:
is_new = env.state.is_new()

# Check if this file has any updated templates.
if is_new or env.is_updated(file):
if args.force or is_new or env.is_updated(file):
template = env.templates_by_name[file.template]

# If a template doesn't require rendering, use it as-is.
Expand All @@ -44,26 +44,40 @@ def apply_env(env: Environment) -> int:
path_fd.write(template.template.render(env.state.configs))
env.logger.info("Rendered '%s'.", rel(source))

# Update the output file.
file.update(source, env.logger)
# Update the output file.
if not args.dry_run:
file.update(source, env.logger)

return result


def apply_cmd(_: _Namespace) -> int:
def apply_cmd(args: _Namespace) -> int:
"""Execute the apply command."""

result = 1

with log_time(getLogger(__name__), "Command"):
with load_environment() as env:
if env.config_loaded:
result = apply_env(env)
result = apply_env(args, env)

return result


def add_apply_cmd(_: _ArgumentParser) -> _CommandFunction:
def add_apply_cmd(parser: _ArgumentParser) -> _CommandFunction:
"""Add apply-command arguments to its parser."""

parser.add_argument(
"-f",
"--force",
action="store_true",
help="whether or not to forcibly render all outputs",
)
parser.add_argument(
"-d",
"--dry-run",
action="store_true",
help="whether or not to update output files",
)

return apply_cmd
58 changes: 3 additions & 55 deletions rcmpy/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,22 @@
"""

# built-in
from contextlib import suppress
from dataclasses import dataclass
from os.path import expandvars
from pathlib import Path
from shutil import copyfile
from typing import Any, Dict, List, Set, cast

# third-party
from vcorelib.dict.codec import BasicDictCodec as _BasicDictCodec
from vcorelib.io.types import JsonObject as _JsonObject
from vcorelib.logging import LoggerType
from vcorelib.paths import rel

# internal
from rcmpy.config.file import ManagedFile
from rcmpy.schemas import RcmpyDictCodec as _RcmpyDictCodec


@dataclass
class ManagedFile:
"""
A data structure for managed files specified in the configuration data.
"""

template: str
extra_templates: Set[str]

directory: Path
name: str

link: bool

@property
def output(self) -> Path:
"""Get the full output path."""
return self.directory.joinpath(self.name)

def update_root(self, root: Path) -> None:
"""
If the output directory is a relative path, update it to be an
absolute one based on the provided root directory.
"""

if not self.directory.is_absolute():
self.directory = root.joinpath(self.directory)
assert self.directory.is_absolute(), self.directory

def update(self, source: Path, logger: LoggerType) -> None:
"""Update this managed file based on the provided source file."""

output = self.output

# At some point this could be skipped for existing symlinks.
with suppress(FileNotFoundError):
output.unlink()

# Ensure the output directory exists.
self.directory.mkdir(parents=True, exist_ok=True)

if self.link:
output.symlink_to(source)
else:
copyfile(source, output)

logger.info("'%s' -> '%s'.", rel(source), rel(output))


FilesConfig = List[Dict[str, Any]]

__all__ = ["FilesConfig", "Config", "ManagedFile"]


class Config(_RcmpyDictCodec, _BasicDictCodec):
"""The top-level configuration object for the package."""
Expand Down
63 changes: 63 additions & 0 deletions rcmpy/config/file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
A module implementing an interface for package-managed files.
"""

# built-in
from contextlib import suppress
from dataclasses import dataclass
from pathlib import Path
from shutil import copyfile
from typing import Set

# third-party
from vcorelib.logging import LoggerType
from vcorelib.paths import rel


@dataclass
class ManagedFile:
"""
A data structure for managed files specified in the configuration data.
"""

template: str
extra_templates: Set[str]

directory: Path
name: str

link: bool

@property
def output(self) -> Path:
"""Get the full output path."""
return self.directory.joinpath(self.name)

def update_root(self, root: Path) -> None:
"""
If the output directory is a relative path, update it to be an
absolute one based on the provided root directory.
"""

if not self.directory.is_absolute():
self.directory = root.joinpath(self.directory)
assert self.directory.is_absolute(), self.directory

def update(self, source: Path, logger: LoggerType) -> None:
"""Update this managed file based on the provided source file."""

output = self.output

# At some point this could be skipped for existing symlinks.
with suppress(FileNotFoundError):
output.unlink()

# Ensure the output directory exists.
self.directory.mkdir(parents=True, exist_ok=True)

if self.link:
output.symlink_to(source)
else:
copyfile(source, output)

logger.info("'%s' -> '%s'.", rel(source), rel(output))
2 changes: 1 addition & 1 deletion rcmpy/data/schemas/ManagedFile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ properties:
# Relative to the data-repository root.
directory:
type: string
default: rcmpy-output
default: rcmpy-out

# Whether or not to create a symbolic link instead of a copy.
link:
Expand Down
3 changes: 3 additions & 0 deletions rcmpy/data/schemas/State.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ properties:
type: string
default: "default"

manifest:
$ref: package://rcmpy/schemas/Config.yaml

previous:
type: object
additionalProperties: false
Expand Down
Loading

0 comments on commit defe9b1

Please sign in to comment.