Skip to content

Commit

Permalink
Add more unit tests and change to Python 3.8 (#14)
Browse files Browse the repository at this point in the history
Added more tests
  • Loading branch information
Gaurav Sheni authored Jul 18, 2022
1 parent 33a4698 commit b4c5280
Show file tree
Hide file tree
Showing 13 changed files with 137 additions and 83 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Set up python 3.7
- name: Set up python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.7
python-version: 3.8
- name: Install codecov and erase coverage
run: |
python -m pip install "$(cat test-requirements.txt | grep codecov)"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Set up python 3.7
- name: Set up python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.7
python-version: 3.8
- name: Run lint check for minimum dependency generator
run: |
python -m pip install -r test-requirements.txt
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Set up python 3.7
- name: Set up python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.7
python-version: 3.8
- name: Run unit tests for minimum dependency generator
run: |
python -m pip install -r requirements.txt
Expand Down
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ clean:

.PHONY: lint
lint:
flake8 minimum_dependency_generator/
isort --check-only minimum_dependency_generator/
black minimum_dependency_generator -t py310 --check

.PHONY: lint-fix
lint-fix:
autopep8 --in-place --recursive --max-line-length=100 minimum_dependency_generator/
black minimum_dependency_generator -t py310
isort minimum_dependency_generator/

.PHONY: test
Expand All @@ -22,3 +22,8 @@ test:
.PHONY: testcoverage
testcoverage:
pytest minimum_dependency_generator/ --cov=minimum_dependency_generator --cov-config=.coveragerc --cache-clear --show-capture=stderr

.PHONY: installdeps
installdeps:
pip install -r requirements.txt
pip install -r test-requirements.txt
43 changes: 28 additions & 15 deletions minimum_dependency_generator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,35 @@


def main():
parser = ArgumentParser(description="reads a requirements file and outputs the minimized requirements")

parser.add_argument('--paths', nargs='+',
help='path for requirements to minimize', required=True)

parser.add_argument('--options', nargs='+', default=None,
help='path for requirements to minimize')

parser.add_argument('--extras_require', nargs='+', default=None,
help='path for requirements to minimize')

parser.add_argument('--output_filepath', default=None,
help='path to output minimum dependencies (optional)')
parser = ArgumentParser(
description="reads a requirements file and outputs the minimized requirements"
)

parser.add_argument(
"--paths", nargs="+", help="path for requirements to minimize", required=True
)

parser.add_argument(
"--options", nargs="+", default=None, help="path for requirements to minimize"
)

parser.add_argument(
"--extras_require",
nargs="+",
default=None,
help="path for requirements to minimize",
)

parser.add_argument(
"--output_filepath",
default=None,
help="path to output minimum dependencies (optional)",
)

args = parser.parse_args()
requirements = generate_min_requirements(args.paths, args.options, args.extras_require, args.output_filepath)
requirements = generate_min_requirements(
args.paths, args.options, args.extras_require, args.output_filepath
)
requirements = sanitize_string(requirements)
# DO NOT remove, the GH action needs to output
print("::set-output name=min_reqs::{}".format(requirements))
Expand All @@ -29,5 +42,5 @@ def sanitize_string(s):
return s.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")


if __name__ == '__main__':
if __name__ == "__main__":
main()
52 changes: 32 additions & 20 deletions minimum_dependency_generator/minimum_dependency_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,19 @@ def remove_comment(requirement):


def is_requirement_path(requirement):
if '.txt' in requirement and '-r' in requirement:
if ".txt" in requirement and "-r" in requirement:
return True
return False


def find_operator_version(package, operator):
version = None
matching_versions = []
for x in package.specifier:
if x.operator == operator:
version = x.version
break
return version
matching_versions.append(Specifier(operator + x.version))
# matching_versions is sorted lowest to highest
matching_versions = sorted(matching_versions, key=str)
return matching_versions[-1].version


def determine_package_name(package):
Expand All @@ -51,13 +52,13 @@ def determine_package_name(package):
return name


def clean_section(section):
section = section.split('\n')
def clean_cfg_section(section):
section = section.split("\n")
section = [x for x in section if len(x) > 1]
return section


def find_min_requirement(requirement, python_version="3.7", major_python_version="py3"):
def find_min_requirement(requirement):
if is_requirement_path(requirement):
# skip requirement paths
# ex '-r core_requirements.txt'
Expand All @@ -67,6 +68,7 @@ def find_min_requirement(requirement, python_version="3.7", major_python_version
return
if ">=" in requirement:
# mininum version specified (ex - 'package >= 0.0.4')
number_operators = requirement.count(">=")
package = Requirement(requirement)
version = find_operator_version(package, ">=")
mininum = create_strict_min(version)
Expand All @@ -87,9 +89,9 @@ def find_min_requirement(requirement, python_version="3.7", major_python_version


def clean_list_length_one(item):
if isinstance(item, list) and len(item) == 1 and ' ' in item[0]:
item = item[0].split(' ')
if item == ['']:
if isinstance(item, list) and len(item) == 1 and " " in item[0]:
item = item[0].split(" ")
if item == [""]:
return None
return item

Expand All @@ -113,10 +115,10 @@ def parse_setup_cfg(paths, options, extras_require):
extras_require = clean_list_length_one(extras_require)
if options and len(options) > 0:
for option in options:
requirements += clean_section(config['options'][option])
requirements += clean_cfg_section(config["options"][option])
if extras_require and len(extras_require) > 0:
for extra in extras_require:
requirements += clean_section(config['options.extras_require'][extra])
requirements += clean_cfg_section(config["options.extras_require"][extra])
return requirements


Expand All @@ -129,20 +131,30 @@ def parse_pyproject_toml(paths, options, extras_require):
extras_require = clean_list_length_one(extras_require)
if options and len(options) > 0:
for option in options:
requirements += toml_dict['project'][option]
requirements += toml_dict["project"][option]
if extras_require and len(extras_require) > 0:
for extra in extras_require:
requirements += toml_dict['project']['optional-dependencies'][extra]
requirements += toml_dict["project"]["optional-dependencies"][extra]
return requirements


def generate_min_requirements(paths, options=None, extras_require=None, output_filepath=None):
def generate_min_requirements(
paths, options=None, extras_require=None, output_filepath=None
):
requirements_to_specifier = defaultdict(list)
min_requirements = []

if len(paths) == 1 and paths[0].endswith('.cfg') and os.path.basename(paths[0]).startswith('setup'):
if (
len(paths) == 1
and paths[0].endswith(".cfg")
and os.path.basename(paths[0]).startswith("setup")
):
requirements = parse_setup_cfg(paths, options, extras_require)
elif len(paths) == 1 and paths[0].endswith('.toml') and os.path.basename(paths[0]).startswith('pyproject'):
elif (
len(paths) == 1
and paths[0].endswith(".toml")
and os.path.basename(paths[0]).startswith("pyproject")
):
requirements = parse_pyproject_toml(paths, options, extras_require)
else:
requirements = parse_requirements_text_file(paths)
Expand All @@ -164,15 +176,15 @@ def generate_min_requirements(paths, options=None, extras_require=None, output_f
for req in list(sorted(requirements_to_specifier.values())):
min_package = find_min_requirement(req)
min_requirements.append(str(min_package))
min_requirements = '\n'.join(min_requirements) + '\n'
min_requirements = "\n".join(min_requirements) + "\n"
if output_filepath:
write_file(min_requirements, output_filepath)
return min_requirements


def write_file(data, filepath):
try:
with open(filepath, 'w') as f:
with open(filepath, "w") as f:
f.write(data)
except OSError:
print("Error writing file")
Expand Down
26 changes: 20 additions & 6 deletions minimum_dependency_generator/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,20 @@ def p_ytest_dep():


@pytest.fixture(scope="session", autouse=True)
def cfg_str(dask_dep, pandas_dep, woodwork_dep, numpy_lower, ploty_dep, numpy_upper, p_ytest_dep):
setup_cfg_str = f'''\
def scipy_lower():
return "scipy >= 1.3.3"


@pytest.fixture(scope="session", autouse=True)
def scipy_even_higher():
return "scipy >= 1.5.0"


@pytest.fixture(scope="session", autouse=True)
def cfg_str(
dask_dep, pandas_dep, woodwork_dep, numpy_lower, ploty_dep, numpy_upper, p_ytest_dep
):
setup_cfg_str = f"""\
[metadata]
name = example_package
Expand All @@ -65,13 +77,15 @@ def cfg_str(dask_dep, pandas_dep, woodwork_dep, numpy_lower, ploty_dep, numpy_up
{numpy_upper}
test =
{p_ytest_dep}
'''
"""
return setup_cfg_str


@pytest.fixture(scope="session", autouse=True)
def toml_cfg(pandas_dep, woodwork_dep, numpy_upper, ploty_dep, p_ytest_dep, dask_dep, numpy_lower):
pyproject_str = f'''\
def toml_cfg(
pandas_dep, woodwork_dep, numpy_upper, ploty_dep, p_ytest_dep, dask_dep, numpy_lower
):
pyproject_str = f"""\
[project]
name = "example_package"
requires-python = ">=3.7,<3.10"
Expand All @@ -90,5 +104,5 @@ def toml_cfg(pandas_dep, woodwork_dep, numpy_upper, ploty_dep, p_ytest_dep, dask
"{dask_dep}",
"{numpy_lower}",
]
'''
"""
return pyproject_str
20 changes: 9 additions & 11 deletions minimum_dependency_generator/tests/test_cfg_toml.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@

@pytest.mark.parametrize(
"file_prefix,file_extension,options",
[('setup', 'cfg', 'install_requires'), ('pyproject', 'toml', 'dependencies')],
[("setup", "cfg", "install_requires"), ("pyproject", "toml", "dependencies")],
)
def test_with_toml_cfg(
file_prefix, file_extension, options, cfg_str, toml_cfg
):
def test_with_toml_cfg(file_prefix, file_extension, options, cfg_str, toml_cfg):
file_str = toml_cfg
if file_prefix == 'setup':
if file_prefix == "setup":
file_str = cfg_str
with tempfile.NamedTemporaryFile(
mode="w", suffix="." + file_extension, prefix=file_prefix
Expand All @@ -23,17 +21,17 @@ def test_with_toml_cfg(

paths = [pyproject_file.name]
options = [options]
extra_requires = ['test dev']
extra_requires = ["test dev"]
min_requirements = generate_min_requirements(paths, options, extra_requires)
verify_min_reqs_cfg_toml(min_requirements)


def verify_min_reqs_cfg_toml(min_requirements):
assert '-r' not in min_requirements
assert '.txt' not in min_requirements
assert 'core-requirements.txt' not in min_requirements
min_requirements = min_requirements.split('\n')
assert min_requirements[-1] == ''
assert "-r" not in min_requirements
assert ".txt" not in min_requirements
assert "core-requirements.txt" not in min_requirements
min_requirements = min_requirements.split("\n")
assert min_requirements[-1] == ""
min_requirements = min_requirements[:-1]
expected_min_reqs = [
"dask[dataframe]==2.30.0",
Expand Down
7 changes: 7 additions & 0 deletions minimum_dependency_generator/tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ def test_lower_upper_bound(dask_dep):
verify_mininum(mininum_package, "dask", "2.30.0", required_extra="dataframe")


def test_two_lower_bounds():
mininum_package = find_min_requirement("scipy>=1.3.3,>=1.5.0")
verify_mininum(mininum_package, "scipy", "1.5.0")
mininum_package = find_min_requirement("scipy>=1.5.0,>=1.3.3")
verify_mininum(mininum_package, "scipy", "1.5.0")


def test_spacing():
mininum_package = find_min_requirement("statsmodels >= 0.12.2")
verify_mininum(mininum_package, "statsmodels", "0.12.2")
Expand Down
Loading

0 comments on commit b4c5280

Please sign in to comment.