Skip to content
Snippets Groups Projects
Commit 6edcac0d authored by Nathan Hertz's avatar Nathan Hertz
Browse files

Committing setup_to_meta changes again because I'm dumb.

parent 4b588b1c
No related branches found
No related tags found
No related merge requests found
import setuptools, importlib, subprocess, os, re import setuptools, importlib, subprocess, os, re
def write_metafile(metadata, filename): def write_metafile(metadata, filepath):
"""
Writes given metadata to file with given path.
"""
try: try:
os.makedirs(filename[:-10]) os.makedirs(filepath[:-10])
except FileExistsError: except FileExistsError:
pass pass
with open(filename, 'w') as f: with open(filepath, 'w') as f:
f.write(metadata) f.write(metadata)
def generate_metafile(setup_dict, path): def generate_metadata(setup_dict, path):
metadata = ( """
'{{% set name = "{name}" %}}\n' Uses given info extracted from setup.py file to fill out metadata template.
'{{% set version = "{version}" %}}\n' :param setup_dict: Dictionary of info extracted from setup.py.
'\n' :param path: Path to root directory of the subproject.
'package:\n' :return: String of metadata, ready to be written to a file.
' name: "{{{{ name|lower }}}}"\n' """
' version: "{{{{ version }}}}"\n' def fmt_ep():
'\n' """
'build:\n' Format entry points section of metadata.
).format( :return: Formatted string if entry points exists; else empty string.
name=setup_dict["name"], """
version=setup_dict["version"] ep_string = ''
if 'entry_points' in setup_dict.keys():
ep_string += 'entry_points:\n'
for ep in setup_dict['entry_points']:
ep_string += ' - {}\n'.format(ep)
return ep_string
def fmt_reqs():
"""
Format requirements section of metadata.
:return: Formatted string if requirements exists; else empty string.
"""
reqs_string = ''
if 'install_requires' in setup_dict.keys():
reqs_string += 'requirements:\n'
build_reqs = ' build:\n'
run_reqs = ' run:\n'
for req in setup_dict['install_requires'].split(', '):
build_reqs += ' - {}\n'.format(req)
run_reqs += ' - {}\n'.format(req)
reqs_string += build_reqs + run_reqs
return reqs_string
def fmt_test():
"""
Format test section of metadata.
:return: Formatted string if tests_require exists; else empty string.
"""
test_string = ''
if 'tests_require' in setup_dict.keys():
test_string += (
'test:\n'
' source_files:\n'
' - test/\n'
' requires:\n'
)
for req in setup_dict['tests_require'].split(', '):
test_string += ' - {}\n'.format(req)
test_string += (
' commands:\n'
' - pytest -vv --log-level=DEBUG --showlocals\n'
)
return test_string
name = setup_dict['name']
version = setup_dict['version']
entry_points = fmt_ep()
path = path.replace("./", "")
requirements = fmt_reqs()
test = fmt_test()
license = setup_dict['license']
summary = setup_dict['description']
with open('tools/metafile_template.txt', 'r') as f:
metadata = f.read()
return metadata.format(
name = name,
version = version,
entry_points = entry_points,
path = path,
requirements = requirements,
test = test,
license = license,
summary = summary
) )
if 'entry_points' in setup_dict.keys():
metadata += ' entry_points:\n'
for ep in setup_dict['entry_points']:
metadata += ' - {}\n'.format(ep)
metadata += (' script: {{{{PYTHON}}}} setup.py install\n'
'\n'
'source:\n'
' path: {}\n'.format(path))
metadata += '\n'
if 'install_requires' in setup_dict.keys():
metadata += 'requirements:\n'
build_reqs = ' build:\n'
run_reqs = ' run:\n'
for req in setup_dict['install_requires'].split(', '):
build_reqs += ' - {}\n'.format(req)
run_reqs += ' - {}\n'.format(req)
metadata += build_reqs
metadata += run_reqs
if 'tests_require' in setup_dict.keys():
metadata += ('test:\n'
' source_files:\n'
' - test/\n'
' requires:\n')
for req in setup_dict['tests_require'].split(', '):
metadata += ' - {}\n'.format(req)
metadata += (' commands:\n'
' - pytest -vv --log-level=DEBUG --showlocals\n')
metadata += ('\n'
'about:\n'
' license: "{0}"\n'
' license_family: "{0}"\n'
' summary: "{1}"'.format(setup_dict['license'], setup_dict['description']))
return metadata
def data_to_dict(setup_data): def data_to_dict(setup_data):
"""
Translates parsed setup.py data from unformatted list to formatted dictionary.
:param setup_data: Data extracted from setup.py using parse_setup.py
:return: Dictionary of formatted data.
"""
data_dict = {} data_dict = {}
r_entry = r"([a-z-_]+): " r_entry = r"([a-z-_]+): "
r_id = r"[a-zA-Z0-9-_]" r_id = r"[a-zA-Z0-9-_]"
...@@ -85,30 +116,59 @@ def data_to_dict(setup_data): ...@@ -85,30 +116,59 @@ def data_to_dict(setup_data):
value = re.match(r_ep, value).group('values') value = re.match(r_ep, value).group('values')
value = re.split(',\s*', value) value = re.split(',\s*', value)
data_dict[field] = value data_dict[field] = value
# print("{}: {}".format(field, value))
return data_dict return data_dict
def parse_setup(d):
"""
Function for running parse_setup.py on each directory with a setup.py file.
NOTE: Contains a hack for getting parse_setup.py to run in each directory.
:param d: Directory with a setup.py file.
:return: Data collected from parse_setup.py.
"""
subprocess.run(['cp', 'tools/parse_setup.py', d])
os.chdir(d)
proc = subprocess.run(['python3', 'parse_setup.py'], stdout=subprocess.PIPE)
setup_data = (proc.stdout.decode('utf-8')).split('\n')
os.chdir(root)
subprocess.run(['rm', '{}/parse_setup.py'.format(d)])
return setup_data
def get_outputs(names): def get_outputs(names):
"""
Generate list of metadata files that will be created.
:param names: List of names of all subprojects with a setup.py file.
:return: List of paths to output files as strings.
"""
outputs = [] outputs = []
for name in names: for name in names:
outputs.append("support/conda/{}/meta.yaml".format(name)) outputs.append("metadata/{}/meta.yaml".format(name))
return outputs return outputs
def get_dirs(): def get_dirs():
find = subprocess.run(['find', '.', '-name', 'setup.py'], stdout=subprocess.PIPE) """
Finds all subdirectories containing setup.py files.
:return: List of directories as strings.
"""
find = subprocess.run([
'find', '.', '-name', 'setup.py', '-not', '-path', './src/*'
], stdout=subprocess.PIPE)
dirs = find.stdout.decode('utf-8').split('\n') dirs = find.stdout.decode('utf-8').split('\n')
dirs_cpy = dirs
for i, d in enumerate(dirs): for i, d in enumerate(dirs_cpy):
if d.find("./src") >= 0:
dirs.remove(d)
continue
dirs[i] = d.replace('/setup.py', '') dirs[i] = d.replace('/setup.py', '')
return dirs return dirs
def get_names(dirs): def get_names(dirs):
"""
Generate list of subproject names based on the rule that the name of the
subproject directory will be the name of the subproject.
:return: List of names as strings.
"""
names = [] names = []
for d in dirs: for d in dirs:
if d != '': if d != '':
...@@ -122,13 +182,33 @@ def get_names(dirs): ...@@ -122,13 +182,33 @@ def get_names(dirs):
return names return names
def del_substrings(s, substrings): def del_substrings(s, substrings):
"""
Function for deleting multiple substrings from a string.
:param s: String to be modified.
:param substrings: List of substrings to be targeted for deletion.
:return: Modified string.
"""
for replace in substrings: for replace in substrings:
s = s.replace(replace, '') s = s.replace(replace, '')
return s return s
root = os.getcwd()
class Recipe: class Recipe:
"""
Buildout Recipe class.
For more detailed information, see the link.
http://www.buildout.org/en/latest/topics/writing-recipes.html
"""
def __init__(self, buildout, name, options): def __init__(self, buildout, name, options):
"""
Initializes fields needed for recipe.
:param buildout: (Boilerplate) Dictionary of options from buildout section
of buildout.cfg
:param name: (Boilerplate) Name of section that uses this recipe.
:param options: (Boilerplate) Options of section that uses this recipe.
"""
self.dirs = get_dirs() self.dirs = get_dirs()
self.names = get_names(self.dirs) self.names = get_names(self.dirs)
self.outputs = get_outputs(self.names) self.outputs = get_outputs(self.names)
...@@ -136,19 +216,20 @@ class Recipe: ...@@ -136,19 +216,20 @@ class Recipe:
# TODO: Keep track of path in setup_dict # TODO: Keep track of path in setup_dict
def install(self): def install(self):
root = os.getcwd() """
Install method that runs when recipe has components it needs to install.
:return: Paths to files, as strings, created by the recipe.
"""
for i, d in enumerate(self.dirs): for i, d in enumerate(self.dirs):
if d != '': if d != '':
os.chdir(d) setup_data = parse_setup(d)
proc = subprocess.run(['python3', 'parse_setup.py'], stdout=subprocess.PIPE)
os.chdir(root)
setup_data = (proc.stdout.decode('utf-8')).split('\n')
data_dict = data_to_dict(setup_data) data_dict = data_to_dict(setup_data)
metadata = generate_metafile(data_dict, d) metadata = generate_metadata(data_dict, d)
write_metafile(metadata, self.outputs[i]) write_metafile(metadata, self.outputs[i])
# Pass created file into options.created()
self.options.created(self.outputs[i]) self.options.created(self.outputs[i])
return self.options.created() return self.options.created()
# No special procedure for updating vs. installing
update = install update = install
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment