Skip to content

Commit a33f615

Browse files
authored
188 ant 1 (#205)
* #188 - Save point * #188 - Save point * 188 - Save point * 188 - Save point * 188 - Save point * #188 - Save point * #188 - Save point * #188 - Save point
1 parent 3ff4a31 commit a33f615

15 files changed

Lines changed: 499 additions & 582 deletions

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning].
77

88
## [Unreleased]
99

10+
## [0.0.24] - 2025-06-20
11+
12+
### Added in 0.0.24
13+
14+
- sz_update_project can now upgrade V3 -> V4 and V4 -> V4 projects
15+
16+
### Changed in 0.0.24
17+
18+
- Update tools from get_configs() to get_config_registry() (SDK change)
19+
- sz_command now uses the setting and command `scroll` instead of `page`
20+
21+
### Fixed in 0.0.24
22+
23+
- sz_create_project shouldn't copy sz_update_project to a new project
24+
1025
## [0.0.23] - 2025-06-12
1126

1227
### Fixed in 0.0.23

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ disable = [
4141
"consider-using-f-string",
4242
"line-too-long",
4343
"too-many-branches",
44-
"too-many-locals"
44+
"too-many-instance-attributes",
45+
"too-many-locals",
46+
"too-many-statements"
4547
]
4648
good-names = [
4749
"template-python"

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
senzing==0.2.2
1+
senzing==0.2.16
22
senzing-core==0.3.15

setup.cfg

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = python-tools
3-
version = 0.0.23
3+
version = 0.0.24
44
author = senzing
55
author_email = support@senzing.com
66
description = Python Tools
@@ -21,8 +21,8 @@ package_dir =
2121
packages = find:
2222
python_requires = >=3.9
2323
install_requires =
24-
senzing >= 0.2.12
25-
senzing-core >= 0.3.11
24+
senzing >= 0.2.16
25+
senzing-core >= 0.3.15
2626

2727
[options.packages.find]
2828
where = src

sz_tools/_project_helpers.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
"""Helpers for creating and updating projects"""
2+
3+
import json
4+
import shutil
5+
from dataclasses import dataclass, field
6+
from pathlib import Path
7+
from typing import Any
8+
9+
from packaging import version as p_version
10+
11+
V3_BACKUP_PATH = "v3_to_v4_upgrade_backups"
12+
V4_BUILD = "szBuildVersion.json"
13+
SZ_SYS_PATH = Path("/opt/senzing")
14+
V4_SYS_PATH = SZ_SYS_PATH / "er"
15+
V4_DATA_PATH = SZ_SYS_PATH / "data"
16+
V4_SYS_BUILD = V4_SYS_PATH / V4_BUILD
17+
18+
COPY_TO_PROJ = {
19+
"er/": {"files": ["*"], "excludes": ["sz_create_project", "sz_update_project", "_project_helpers.py"]},
20+
"data": {"files": ["*"], "excludes": []},
21+
}
22+
23+
PERMISSIONS = {
24+
".": {
25+
"dir_pint": 0,
26+
"file_pint": 0o660,
27+
"files": ["LICENSE", "NOTICES", "README.1ST", "szBuildVersion.json"],
28+
"excludes": ["setupEnv"],
29+
"recursive": False,
30+
},
31+
"setupEnv": {"dir_pint": 0, "file_pint": 0o770, "files": [], "excludes": [], "recursive": False},
32+
"bin": {
33+
"dir_pint": 0o770,
34+
"file_pint": 0o770,
35+
"files": ["*"],
36+
"excludes": ["__pycache__", "_sz_database.py", "_tool_helpers.py"],
37+
"recursive": False,
38+
},
39+
"data": {"dir_pint": 0o770, "file_pint": 0o660, "files": ["*"], "excludes": [], "recursive": True},
40+
"lib": {"dir_pint": 0o770, "file_pint": 0o660, "files": ["*"], "excludes": [], "recursive": False},
41+
"resources": {"dir_pint": 0o770, "file_pint": 0o660, "files": ["*"], "excludes": ["setupEnv"], "recursive": True},
42+
"resources/templates/setupEnv": {
43+
"dir_pint": 0,
44+
"file_pint": 0o770,
45+
"files": [],
46+
"excludes": [],
47+
"recursive": False,
48+
},
49+
"sdk": {"dir_pint": 0o770, "file_pint": 0o660, "files": ["*"], "excludes": [], "recursive": True},
50+
V3_BACKUP_PATH: {"dir_pint": 0o770, "file_pint": 0, "files": [], "excludes": [], "recursive": False},
51+
}
52+
53+
PERMISSIONS_2 = {
54+
"bin": {
55+
"dir_pint": 0o770,
56+
"file_pint": 0o660,
57+
"files": ["_sz_database.py", "_tool_helpers.py"],
58+
"excludes": [],
59+
"recursive": False,
60+
}
61+
}
62+
63+
64+
@dataclass()
65+
class SzBuildDetails:
66+
"""Build information for a project or Senzing SDK system install"""
67+
68+
platform: str
69+
version: str
70+
build_version: str
71+
build_number: str
72+
major: int = field(init=False)
73+
minor: int = field(init=False)
74+
micro: int = field(init=False)
75+
76+
def __post_init__(self) -> None:
77+
self.version_parsed = p_version.parse(self.version)
78+
self.build_version_parsed = p_version.parse(self.build_version)
79+
self.major = self.version_parsed.major
80+
self.minor = self.version_parsed.minor
81+
self.micro = self.version_parsed.micro
82+
83+
84+
def get_build_details(path: Path) -> SzBuildDetails:
85+
"""Return dataclass with the details from a build file."""
86+
try:
87+
with open(path, "r", encoding="utf-8") as f:
88+
# Ignore DATA_VERSION, V3 build files had it V4 doesn't
89+
version_dict = {k.lower(): v for k, v in json.load(f).items() if k != "DATA_VERSION"}
90+
except (OSError, json.JSONDecodeError) as err:
91+
raise OSError(f"Couldn't get the build information from {path}: {err}") from err
92+
93+
return SzBuildDetails(**version_dict)
94+
95+
96+
def copy_files_dirs(to_copy: dict[str, Any], source_dir: Path, target_dir: Path) -> None:
97+
"""Copy files/directories within and to a project"""
98+
for c_path, c_dict in to_copy.items():
99+
excludes = c_dict["excludes"]
100+
files = c_dict["files"]
101+
source = source_dir / c_path
102+
103+
try:
104+
if source.is_dir():
105+
# If the key in to_copy ends with / copy everything in source dir to target_dir
106+
# er/ as the key copies everything from /opt/senzing/er to target_dir
107+
#
108+
# If the key in to_copy doesn't end with / copy source dir and everything in it to target_dir
109+
# data as the key copies /opt/senzing/data to target_dir/data
110+
target = target_dir if c_path.endswith("/") else target_dir / c_path
111+
112+
# Copy entire contents of the source directory
113+
if not files or (files and files[0] == "*"):
114+
shutil.copytree(source, target, ignore=shutil.ignore_patterns(*excludes), dirs_exist_ok=True)
115+
116+
# Create the source directory in the target and only copy listed files
117+
if files and files[0] != "*":
118+
target.mkdir(exist_ok=True, parents=True)
119+
for source_file in [source / f for f in files]:
120+
shutil.copy(source_file, target / source_file.name)
121+
122+
if source.is_file():
123+
# Single file copy always copies only the file, if the key to to_copy is er/szBuildVersion.json
124+
# szBuildVersion.json is copied to target_dir and not target_dir/er/szBuildVersion.json
125+
target = target_dir / source.name
126+
target_dir.mkdir(exist_ok=True, parents=True)
127+
shutil.copy(source, target)
128+
except OSError as err:
129+
raise OSError(f"Couldn't copy a file or directory: {err}") from err
130+
131+
132+
def setup_env(proj_path: Path) -> None:
133+
"""Create a new setupEnv and replace place holders with paths for the project"""
134+
try:
135+
shutil.copy(proj_path / "resources/templates/setupEnv", proj_path)
136+
setup_path = proj_path / "setupEnv"
137+
138+
with open(setup_path, "r", encoding="utf-8") as in_:
139+
data = in_.read()
140+
141+
data = data.replace("${SENZING_DIR}", str(proj_path)).replace("${SENZING_CONFIG_PATH}", str(proj_path / "etc"))
142+
143+
with open(setup_path, "w", encoding="utf-8") as out:
144+
out.write(data)
145+
except OSError as err:
146+
raise OSError(f"Couldn't create a new setupEnv file: {err}") from err
147+
148+
149+
def set_permissions(proj_path: Path, permissions: dict[str, dict[str, Any]]) -> None:
150+
"""Set permissions for files/dirs copied to the project, or dirs removed and replaced completely e.g., data/"""
151+
try:
152+
for p_path, p_dict in permissions.items():
153+
dir_pint = p_dict["dir_pint"]
154+
file_pint = p_dict["file_pint"]
155+
files = p_dict["files"]
156+
recursive = p_dict["recursive"]
157+
target = proj_path if p_path.startswith(".") else proj_path / p_path
158+
excludes = [target / e for e in p_dict["excludes"]]
159+
160+
if target.is_dir():
161+
if dir_pint != 0:
162+
target.chmod(dir_pint)
163+
d_chmods = (
164+
[d for d in target.glob("*") if d.is_dir() and not d.is_symlink() and d not in excludes]
165+
if not recursive
166+
else [d for d in target.rglob("*") if d.is_dir() and not d.is_symlink() and d not in excludes]
167+
)
168+
for dir_ in d_chmods:
169+
dir_.chmod(dir_pint)
170+
171+
if files and files[0] == "*":
172+
f_chmods = (
173+
[f for f in target.glob("*") if f.is_file() and not f.is_symlink() and f not in excludes]
174+
if not recursive
175+
else [f for f in target.rglob("*") if f.is_file() and not f.is_symlink() and f not in excludes]
176+
)
177+
178+
for file in f_chmods:
179+
Path(target / file).chmod(file_pint)
180+
181+
if files and files[0] != "*":
182+
for file in files:
183+
Path(target / file).chmod(file_pint)
184+
185+
if target.is_file():
186+
target.chmod(file_pint)
187+
except OSError as err:
188+
raise OSError(f"Couldn't set a permission: {err}") from err

0 commit comments

Comments
 (0)