Skip to content
Snippets Groups Projects
Commit f4506a16 authored by Daniel Lyons's avatar Daniel Lyons
Browse files

Refactor and remove numpy dependencies

 - Make communication between parse_setup and setup_to_meta JSON
 - Refactor generate_metadata() into class
 - Filter out numpy, because it is implied by Conda
parent a037cf3d
No related branches found
No related tags found
No related merge requests found
import json
import setuptools, importlib, subprocess, os, re
PYTHON_VERSION = '3.8'
......@@ -14,46 +16,51 @@ def write_metafile(metadata, filepath):
with open(filepath, 'w') as f:
f.write(metadata)
def generate_metadata(setup_dict, path):
class MetadataGenerator:
"""
Uses given info extracted from setup.py file to fill out metadata template.
:param setup_dict: Dictionary of info extracted from setup.py.
:param path: Path to root directory of the subproject.
:return: String of metadata, ready to be written to a file.
"""
def fmt_ep():
def __init__(self, setup, path):
self.setup = setup
self.path = path
def fmt_ep(self):
"""
Format entry points section of metadata.
:return: Formatted string if entry points exists; else empty string.
"""
ep_string = ''
if 'entry_points' in setup_dict.keys():
if 'entry_points' in self.setup.keys() and 'console_scripts' in self.setup['entry_points']:
ep_string += 'entry_points:\n'
for ep in setup_dict['entry_points']:
for ep in self.setup['entry_points']['console_scripts']:
ep_string += ' - {}\n'.format(ep)
ep_string += ' '
return ep_string
def fmt_reqs():
def fmt_reqs(self):
"""
Format requirements section of metadata.
:return: Formatted string if requirements exists; else empty string.
"""
reqs_string = ''
reqs_list = ''
if 'install_requires' in setup_dict.keys():
if 'install_requires' in self.setup.keys():
reqs_string += 'requirements:\n'
build_reqs = ' build:\n'
run_reqs = ' run:\n'
host_reqs = ' host:\n'
reqs_list += ' - python={}\n'.format(PYTHON_VERSION)
for req in setup_dict['install_requires'].split(', '):
for req in self.setup['install_requires']:
reqs_list += ' - {}\n'.format(req)
reqs_string += build_reqs + reqs_list + \
run_reqs + reqs_list + \
host_reqs + reqs_list + \
'\n'
return reqs_string
def fmt_test():
def fmt_test(self):
"""
Format test section of metadata.
NOTE: May need further tweaking to be smarter based on individual project
......@@ -61,7 +68,7 @@ def generate_metadata(setup_dict, path):
:return: Formatted string if tests_require exists; else empty string.
"""
test_string = ''
if 'tests_require' in setup_dict.keys():
if 'tests_require' in self.setup.keys():
test_string += (
'test:\n'
' source_files:\n'
......@@ -69,7 +76,7 @@ def generate_metadata(setup_dict, path):
' requires:\n'
)
for req in setup_dict['tests_require'].split(', '):
for req in self.setup['tests_require']:
test_string += ' - {}\n'.format(req)
test_string += (
......@@ -79,55 +86,33 @@ def generate_metadata(setup_dict, path):
)
return test_string
name = setup_dict['name']
version = setup_dict['version']
entry_points = fmt_ep()
pth = path.replace("./", "")
requirements = fmt_reqs()
test = fmt_test()
lic = 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 = "../../" + pth,
requirements = requirements,
test = test,
license = lic,
summary = summary
)
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 = {}
r_entry = r"([a-z-_]+): "
r_id = r"[a-zA-Z0-9-_]"
r_ep = r"{{(?:[a-zA-Z0-9-_.]+):\s*(?P<values>(?:{0}+\s*=\s*[a-zA-Z0-9-_.]+:{0}+(?:,\s*)?)*)}}".format(r_id)
for d in setup_data:
result = re.match(r_entry, d)
if result:
substrings = [result.group(0), '[', ']', '\'', '"']
field = result.group(1)
value = del_substrings(d, substrings)
if value == "None":
continue
if re.match(r_ep, value):
# Parse entry points
value = re.match(r_ep, value).group('values')
value = re.split(',\s*', value)
data_dict[field] = value
return data_dict
def generate(self):
# Filter numpy etc. out of the requirements
self.setup['install_requires'] = [req for req in self.setup['install_requires'] if req != 'numpy']
name = self.setup['name']
version = self.setup['version']
entry_points = self.fmt_ep()
pth = self.path.replace("./", "")
requirements = self.fmt_reqs()
test = self.fmt_test()
lic = self.setup['license']
summary = self.setup['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 = "../../" + pth,
requirements = requirements,
test = test,
license = lic,
summary = summary
)
def parse_setup(d):
"""
......@@ -139,11 +124,10 @@ def parse_setup(d):
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
return json.loads(proc.stdout)
def get_outputs(names):
"""
......@@ -233,8 +217,7 @@ class Recipe:
for i, d in enumerate(self.dirs):
if d != '':
setup_data = parse_setup(d)
data_dict = data_to_dict(setup_data)
metadata = generate_metadata(data_dict, d)
metadata = MetadataGenerator(setup_data, d).generate()
write_metafile(metadata, self.outputs[i])
# Pass created file into options.created()
self.options.created(self.outputs[i])
......
import sys
import setuptools
def get_data(field, data):
"""
Get data from field if it exists.
:return: None if not found. Datum of field if found.
"""
datum = None
try:
datum = data[field]
except KeyError as e:
pass
return datum
import json
data = {}
def my_setup(*args, **kwargs):
......@@ -28,17 +18,23 @@ def my_setup(*args, **kwargs):
]
for field in fields:
data[field] = get_data(field, kwargs)
data[field] = kwargs.get(field)
def main():
# Author of these shenanigans: Daniel Lyons (but you already knew that)
global data
# Monkey-patch over the setuptools setup() function to do our function instead
setuptools.setup = my_setup
# Load the setup.py file
import setup
# Remove None-flavored values
data = {k: v for k, v in data.items() if v is not None}
# Instead of exiting, we now have populated our global variable, without doing any parsing
for field, datum in data.items():
print('{}: {}'.format(field, datum))
json.dump(data, sys.stdout)
if __name__ == "__main__":
main()
\ 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