This repository has been archived by the owner on Sep 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
py_make_name.py
239 lines (182 loc) · 7.12 KB
/
py_make_name.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import sys
import pathlib
import os
import shutil
import re
from itertools import count
from glob import iglob
from dataclasses import dataclass
from configparser import ConfigParser
from typing import Optional
"""
changes name of module in file path file path directory and all relevant config settings
"""
@dataclass(init=False)
class ScriptInfo:
"""
class for holding script parameters and moving data between functions
"""
# names
name_target: Optional[str] = None
name_original: Optional[str] = None
# paths
path_original: Optional[pathlib.Path] = None
path_target: Optional[pathlib.Path] = None
# flags
new_created: bool = False
@staticmethod
def config_path() -> pathlib.Path:
"""path to configuration file in current working directory"""
return pathlib.Path("./setup.cfg").absolute()
def load_cfg(config_path: pathlib.Path) -> ConfigParser:
"""
loads library config file
:return: loaded `ConfigParser` object
"""
config = ConfigParser()
config.read(str(config_path))
return config
def load_target_name(script_info: ScriptInfo) -> None:
"""
loads target name from system arguments into script info, raises errors if value is
incorrect
:param script_info:
:return:
"""
# throw error if new name was not passed
try:
script_info.name_target = sys.argv[1]
except IndexError:
raise ValueError("new name must be passed with name=[name] param")
# throw error if target name is empty
if not script_info.name_target:
raise ValueError("new name must be passed with name=[name] param")
def make_new_directory(script_info: ScriptInfo) -> None:
# load current and new paths
script_info.path_original = pathlib.Path(".").absolute()
script_info.path_target = (
script_info.path_original.parent / f"{script_info.name_target}-py"
)
# throw error if new library directory already exists
if script_info.path_target.exists():
raise FileExistsError(f"directory {script_info.path_target} exists")
# create new directory and copy current contents
shutil.copytree(str(script_info.path_original), str(script_info.path_target))
# switch this flag to show the high-level error catcher that the new directory
# has been made and will need to be removed in cleanup of a later exception is
# caught
script_info.new_created = True
def edit_cfg(script_info: ScriptInfo) -> str:
"""
edits setup.cfg with new name of library in necessary fields
:param script_info: script info object
"""
target_name = script_info.name_target
config = load_cfg(script_info.config_path())
old_name = config.get("metadata", "name")
config.set("metadata", "name", target_name)
config.set("coverage:run", "source", target_name)
config.set("coverage:html", "title", f"coverage report for {target_name}")
config.set("build_sphinx", "project", target_name)
with open(str(script_info.config_path()), mode="w") as f:
config.write(f)
return old_name
def rewrite_sphinx_conf(target_name: str) -> None:
"""
writes sphinx conf.py with new lib name for documentation settings
:param target_name: new name of library
:return:
"""
# there is template file we can perform a simple find/replace on to change the
# name of the lib where necessary
template_path = pathlib.Path("./zdocs/source/conf-template").absolute()
conf_path = pathlib.Path("./zdocs/source/conf.py").absolute()
template_text = template_path.read_text()
conf_text = template_text.replace("{lib-name-goes-here}", target_name)
conf_path.write_text(conf_text)
def rename_packages(old_name: str, target_name: str) -> None:
"""
renames top level directory, module package, and changes active directory to it
:param old_name: old name of lib
:param target_name: new name of lib
:return:
"""
# find current lib path - look for the init and ignore zdevelop
search_pattern = "./*/__init__.py"
# iterate through init statements in current directory and rename parents
i = None
for init_path, i in zip(iglob(search_pattern, recursive=True), count(1)):
parent_path: pathlib.Path = pathlib.Path(init_path).parent
parent_name = parent_path.name
if parent_name.lower() == "zdevelop":
continue
# if this lib has multiple packages, we may need to sub-out the name
if old_name.lower() in parent_name.lower():
new_name = re.sub(parent_name, old_name, target_name, flags=re.IGNORECASE)
# otherwise, if the name is unrelated it just gets renamed to the new one
else:
new_name = target_name
target_path = parent_path.with_name(new_name)
# rename module folder name
try:
parent_path.rename(target_path)
except FileExistsError as this_error:
sys.stderr.write(
f"package '{target_name}' already exists, your current package names"
f"may not conform to illuscio's standards. All packages names should "
f"contain the root name of the library"
)
raise this_error
if i is None:
raise FileNotFoundError("no packages found in library")
def alter_new(script_info: ScriptInfo) -> None:
"""
renames lib and writes 1 or 0 to stdout for whether .egg needs to be
rewritten
"""
# edit the config file and get current name
old_name = edit_cfg(script_info)
# write new conf.py
assert script_info.name_target is not None
rewrite_sphinx_conf(script_info.name_target)
# remove .egg info
path_egg = f"{old_name}.egg-info"
try:
shutil.rmtree(path_egg)
except PermissionError:
os.chmod(path_egg, mode=0o007)
shutil.rmtree(path_egg)
except FileNotFoundError:
pass
# rename directory
rename_packages(old_name, script_info.name_target)
def main() -> None:
"""makes new directory and handles errors"""
script_info = ScriptInfo()
try:
# cor logic of the script, wrapped in try/except to handle directory cleanup
load_target_name(script_info)
make_new_directory(script_info)
# change working directory to new directory
os.chdir(str(script_info.path_target))
# make alterations to new directory
alter_new(script_info)
# write result path to srd out so make file can change working directory
sys.stdout.write(str(script_info.path_target))
except BaseException as this_error:
# if there are any errors and the new directory path was created during this
# script, we need to clean it up before aborting
if script_info.new_created:
shutil.rmtree(str(script_info.path_target))
raise this_error
else:
# if all alterations to the new directory go as planned, we can remove the old
# directory
shutil.rmtree(str(script_info.path_original))
if __name__ == "__main__":
try:
main()
except BaseException as error:
# tell Make script not to continue
sys.stdout.write("0")
raise error