-
Notifications
You must be signed in to change notification settings - Fork 0
/
packager_files.py
204 lines (158 loc) · 8.15 KB
/
packager_files.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
from generallibrary import CodeLine, current_datetime, Markdown
from generalfile import Path
class GenerateFile:
""" Handle generation of files. """
def __init__(self, path, text_func, packager, aesthetic):
self.text_func = text_func
self.packager = packager
self.aesthetic = aesthetic
self.relative_path = path.relative(base=packager.path)
self.path = packager.path / self.relative_path
def generate(self):
""" Generate actual file. """
self.path.text.write(f"{self.text_func()}\n", overwrite=True)
class _PackagerFiles:
""" Generates setup, license and gitexclude.
Only changed non-aesthetic files can trigger a version bump and publish. """
extra_aesthetic = "randomtesting.py", # "licenses"
extra_non_aesthetic = tuple()
def __init_post__(self):
""" Todo: Watermark generated files to prevent mistake of thinking you can modify them directly.
:param generalpackager.Packager self: """
self.file_setup = GenerateFile(self.localrepo.get_setup_path(), self.generate_setup, self, aesthetic=False)
self.file_manifest = GenerateFile(self.localrepo.get_manifest_path(), self.generate_manifest, self, aesthetic=False)
self.file_git_exclude = GenerateFile(self.localrepo.get_git_exclude_path(), self.generate_git_exclude, self, aesthetic=True)
self.file_license = GenerateFile(self.localrepo.get_license_path(), self.generate_license, self, aesthetic=True)
self.file_workflow = GenerateFile(self.localrepo.get_workflow_path(), self.generate_workflow, self, aesthetic=True)
self.file_readme = GenerateFile(self.localrepo.get_readme_path(), self.generate_readme, self, aesthetic=True)
self.files = [getattr(self, key) for key in dir(self) if key.startswith("file_")]
self.files_by_relative_path = {file.relative_path: file for file in self.files}
def relative_path_is_aesthetic(self, relative_path):
""" Relative to package path. None if not defined as a GenerateFile instance.
:param generalpackager.Packager self:
:param Path relative_path: """
aesthetic_attr = getattr(self.files_by_relative_path.get(relative_path, None), "aesthetic", None)
if aesthetic_attr is None:
if relative_path.match(*self.extra_aesthetic):
return True
elif relative_path.match(*self.extra_non_aesthetic):
return False
return aesthetic_attr
def filter_relative_filenames(self, *filenames, aesthetic, non_defined=True):
""" If aesthetic is None then it doesn't filter any.
True will return only aesthetic.
False will return only non-aesthetic.
:param generalpackager.Packager self:
:param bool or None aesthetic:
:param bool non_defined: """
result = []
for filename in filenames:
is_aesthetic = self.relative_path_is_aesthetic(filename)
if non_defined is False and is_aesthetic is None:
continue
if aesthetic is True and is_aesthetic is False:
continue
if aesthetic is False and is_aesthetic is True:
continue
result.append(filename)
return result
def compare_local_to_remote(self, aesthetic=None):
""" Get a list of changed files compared to remote with optional aesthetic files.
:param generalpackager.Packager self:
:param aesthetic: """
return self.filter_relative_filenames(*self.localrepo.get_changed_files(), aesthetic=aesthetic)
def generate_setup(self):
""" Generate setup.py.
:param generalpackager.Packager self: """
readme_path = self.localrepo.get_readme_path().relative(self.localrepo.get_setup_path().get_parent())
last_version_split = self.python[-1].split(".")
last_version_bumped_micro = f"{last_version_split[0]}.{int(last_version_split[1]) + 1}"
setup_kwargs = {
"name": f'"{self.localrepo.name}"',
"author": f"'{self.author}'",
"author_email": f'"{self.email}"',
"version": f'"{self.localrepo.version}"',
"description": f'"{self.localrepo.description}"',
"long_description": f"(Path(__file__).parent / '{readme_path}').read_text(encoding='utf-8')",
"long_description_content_type": '"text/markdown"',
"install_requires": self.localrepo.install_requires,
"url": f'"{self.github.url}"',
"license": f'"{self.license}"',
"python_requires": f'">={self.python[0]}, <{last_version_bumped_micro}"',
"packages": 'find_namespace_packages(exclude=("build*", "dist*"))',
"extras_require": self.localrepo.extras_require,
"classifiers": self.get_classifiers(),
# "include_package_data": True,
}
top = CodeLine()
top.add(CodeLine("from setuptools import setup, find_namespace_packages", space_before=1))
top.add(CodeLine("from pathlib import Path", space_after=1))
setup = top.add(CodeLine("setup("))
for key, value in setup_kwargs.items():
if isinstance(value, list) and value:
list_ = setup.add(CodeLine(f"{key}=["))
for item in value:
list_.add(CodeLine(f"'{item}',"))
setup.add(CodeLine("],"))
elif isinstance(value, dict) and value:
dict_ = setup.add(CodeLine(f"{key}={{"))
for k, v in value.items():
dict_.add(CodeLine(f"'{k}': {v},"))
setup.add(CodeLine("},"))
else:
setup.add(CodeLine(f"{key}={value},"))
top.add(CodeLine(")"))
return top.text()
def generate_manifest(self):
""" Generate manifest file.
:param generalpackager.Packager self: """
default_manifest = [
self.localrepo.get_metadata_path().relative(self.path),
]
return "\n".join([f"include {path}" for path in self.localrepo.manifest + default_manifest])
def generate_git_exclude(self):
""" Generate git exclude file.
:param generalpackager.Packager self: """
return "\n".join(self.git_exclude_lines)
def generate_license(self):
""" Generate LICENSE by using Packager.license.
:param generalpackager.Packager self: """
text = Path(self.repos_path / f"generalpackager/generalpackager/licenses/{self.license}").text.read()
assert "$" in text
text = text.replace("$year", str(current_datetime().year))
text = text.replace("$author", self.author)
assert "$" not in text
return text
def generate_workflow(self):
""" Generate workflow.yml.
:param generalpackager.Packager self: """
workflow = CodeLine()
workflow.indent_str = " " * 2
workflow.add("name: workflow")
workflow.add(self.get_triggers())
jobs = workflow.add("jobs:")
jobs.add(self.get_unittest_job())
jobs.add(self.get_sync_job())
return workflow.text()
def generate_readme(self):
""" Generate readme markdown and overwrite README.md in local repo.
:param generalpackager.Packager self: """
# Description
markdown = Markdown(self.localrepo.description, header=f"{self.name} {self.localrepo.version}")
# Badges
markdown.add_lines(*self.get_badges_dict().values())
# Table of contents - Placeholder
contents = Markdown(header="Contents", parent=markdown)
# Installation
self.get_installation_markdown().set_parent(parent=markdown)
# Attributes
self.get_attributes_markdown().set_parent(parent=markdown)
# Todos
todos = self.localrepo.get_todos()
if todos:
Markdown(header="Todo", parent=markdown).add_table_lines(*todos)
# Table of contents - Configuration
self.configure_contents_markdown(markdown=contents)
# Footnote
self.get_footnote_markdown().set_parent(parent=markdown)
return markdown