Skip to content

Commit

Permalink
Transition from using setup.py to pyroject.toml to specify project me…
Browse files Browse the repository at this point in the history
…tadata (#379)
  • Loading branch information
frances-h authored Mar 1, 2024
1 parent ddc6889 commit 0c42168
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 112 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/readme.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install invoke rundoc .
python -m pip install invoke tomli rundoc .
- name: Run the README.md
run: invoke readme
2 changes: 1 addition & 1 deletion .github/workflows/tutorials.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ jobs:
- name: Install package and dependencies
run: |
python -m pip install --upgrade pip
python -m pip install invoke jupyter .[tutorials]
python -m pip install invoke tomli jupyter .[tutorials]
- name: invoke tutorials
run: invoke tutorials
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@ serve-docs: ## compile the docs watching for changes

.PHONY: dist
dist: clean ## builds source and wheel package
python setup.py sdist
python setup.py bdist_wheel
python -m build --wheel --sdist
ls -l dist

.PHONY: publish-confirm
Expand Down
130 changes: 64 additions & 66 deletions setup.py → pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
#.dev0!/usr/bin/env python
# -*- coding: utf-8 -*-

"""The setup script."""

from setuptools import setup, find_packages

with open('README.md', encoding='utf-8') as readme_file:
readme = readme_file.read()

with open('HISTORY.md', encoding='utf-8') as history_file:
history = history_file.read()

install_requires = [
[project]
name = 'copulas'
description = 'Create tabular synthetic data using copulas-based modeling.'
authors = [{name = 'DataCebo, Inc.', email = 'info@sdv.dev' }]
classifiers = [
'Development Status :: 2 - Pre-Alpha',
'Intended Audience :: Developers',
'License :: Free for non-commercial use',
'Natural Language :: English',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Topic :: Scientific/Engineering :: Artificial Intelligence',
]
keywords = [ 'copulas' ]
version = '0.10.1.dev0'
license = { text = 'BSL-1.1' }
requires-python = '>=3.8,<3.12'
readme = 'README.md'
dependencies = [
"numpy>=1.20.0,<2;python_version<'3.10'",
"numpy>=1.23.3,<2;python_version>='3.10'",
"pandas>=1.1.3;python_version<'3.10'",
Expand All @@ -22,7 +30,34 @@
"scipy>=1.9.2,<2;python_version>='3.10'",
]

development_requires = [
[project.urls]
"Source Code"= "https://github.com/sdv-dev/Copulas/"
"Issue Tracker" = "https://github.com/sdv-dev/Copulas/issues"
"Changes" = "https://github.com/sdv-dev/Copulas/blob/main/HISTORY.md"
"Twitter" = "https://twitter.com/sdv_dev"
"Chat" = "https://bit.ly/sdv-slack-invite"

[build-system]
requires = ['setuptools', 'wheel']
build-backend = 'setuptools.build_meta'

[project.optional-dependencies]
tutorials = [
'markupsafe<=2.0.1',
'scikit-learn>=0.24,<1.2',
'jupyter>=1.0.0,<2',
]
test = [
'copulas[tutorials]',
'pytest>=6.2.5,<7',
'pytest-cov>=2.6.0,<3',
'pytest-rerunfailures>=9.0.0,<10',
'rundoc>=0.4.3,<0.5',
'tomli>=2.0.0,<3',
]
dev = [
'copulas[tutorials, test]',

# general
'pip>=9.0.1',
'bumpversion>=0.5.3,<0.6',
Expand Down Expand Up @@ -84,57 +119,20 @@
'docutils>=0.10,<0.15'
]

tutorials_require = [
'markupsafe<=2.0.1',
'scikit-learn>=0.24,<1.2',
'jupyter>=1.0.0,<2',
]
[tool.isort]
line_length = 99
lines_between_types = 0
multi_line_output = 4
use_parentheses = true
not_skip = ['__init__.py']

tests_require = [
'pytest>=6.2.5,<7',
'pytest-cov>=2.6.0,<3',
'pytest-rerunfailures>=9.0.0,<10',
'rundoc>=0.4.3,<0.5',
]
[tool.pydocstyle]
convention = 'google'
add-ignore = ['D107', 'D407', 'D417']

setup_requires = [
'pytest-runner>=2.11.1',
]
[tool.setuptools]
include-package-data = true

setup(
author='DataCebo, Inc.',
author_email='info@sdv.dev',
classifiers=[
'Development Status :: 2 - Pre-Alpha',
'Intended Audience :: Developers',
'License :: Free for non-commercial use',
'Natural Language :: English',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Topic :: Scientific/Engineering :: Artificial Intelligence',
],
description='Create tabular synthetic data using copulas-based modeling.',
extras_require={
'tutorials': tutorials_require,
'test': tests_require + tutorials_require,
'dev': tests_require + development_requires + tutorials_require,
},
install_requires=install_requires,
license='BSL-1.1',
long_description=readme + '\n\n' + history,
long_description_content_type='text/markdown',
include_package_data=True,
keywords='copulas',
name='copulas',
packages=find_packages(include=['copulas', 'copulas.*']),
python_requires='>=3.8,<3.12',
setup_requires=setup_requires,
test_suite='tests',
tests_require=tests_require,
url='https://github.com/sdv-dev/Copulas',
version='0.10.1.dev0',
zip_safe=False,
)
[tool.setuptools.packages.find]
include = ['copulas', 'copulas.*']
namespaces = false
15 changes: 1 addition & 14 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ values =

[bumpversion:part:candidate]

[bumpversion:file:setup.py]
[bumpversion:file:pyproject.toml]
search = version='{current_version}'
replace = version='{new_version}'

Expand All @@ -40,23 +40,10 @@ extend-ignore = D107, # Missing docstring in __init__
per-file-ignores =
large_scale_evaluation.py:T001

[pydocstyle]
convention = google
add-ignore = D107, D407, D417

[isort]
line_length = 99
lines_between_types = 0
multi_line_output = 4
use_parentheses = True
not_skip = __init__.py

[aliases]
test = pytest

[tool:pytest]
collect_ignore = ['setup.py']

[doc8]
max-line-length = 99

72 changes: 44 additions & 28 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import inspect
import operator
import os
import re
import pkg_resources
import platform
import tomli
import sys
from packaging.requirements import Requirement
from packaging.version import Version
import shutil
import stat
from pathlib import Path
Expand Down Expand Up @@ -60,33 +61,48 @@ def _validate_python_version(line):
return is_valid


def _get_minimum_versions(dependencies, python_version):
min_versions = {}
for dependency in dependencies:
if '@' in dependency:
name, url = dependency.split(' @ ')
min_versions[name] = f'{name} @ {url}'
continue

req = Requirement(dependency)
if ';' in dependency:
marker = req.marker
if marker and not marker.evaluate({'python_version': python_version}):
continue # Skip this dependency if the marker does not apply to the current Python version

if req.name not in min_versions:
min_version = next(
(spec.version for spec in req.specifier if spec.operator in ('>=', '==')), None)
if min_version:
min_versions[req.name] = f'{req.name}=={min_version}'

elif '@' not in min_versions[req.name]:
existing_version = Version(min_versions[req.name].split('==')[1])
new_version = next(
(spec.version for spec in req.specifier if spec.operator in ('>=', '==')), existing_version)
if new_version > existing_version:
# Change when a valid newer version is found
min_versions[req.name] = f'{req.name}=={new_version}'

return list(min_versions.values())


@task
def install_minimum(c):
with open('setup.py', 'r') as setup_py:
lines = setup_py.read().splitlines()

versions = []
started = False
for line in lines:
if started:
if line == ']':
break

line = line.strip()
if _validate_python_version(line):
requirement = re.match(r'[^>]*', line).group(0)
requirement = re.sub(r"""['",]""", '', requirement)
version = re.search(r'>=?(\d\.?)+', line).group(0)
if version:
version = re.sub(r'>=?', '==', version)
version = re.sub(r"""['",]""", '', version)
requirement += version
versions.append(requirement)

elif line.startswith('install_requires = ['):
started = True

c.run(f'python -m pip install {" ".join(versions)}')
with open('pyproject.toml', 'rb') as pyproject_file:
pyproject_data = tomli.load(pyproject_file)

dependencies = pyproject_data.get('project', {}).get('dependencies', [])
python_version = '.'.join(map(str, sys.version_info[:2]))
minimum_versions = _get_minimum_versions(dependencies, python_version)

if minimum_versions:
c.run(f'python -m pip install {" ".join(minimum_versions)}')


@task
Expand Down
36 changes: 36 additions & 0 deletions tests/test_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from tasks import _get_minimum_versions


def test_get_minimum_versions():
"""Test the ``_get_minimum_versions`` method.
The method should return the minimum versions of the dependencies for the given python version.
If a library is linked to an URL, the minimum version should be the URL.
"""
# Setup
dependencies = [
"numpy>=1.20.0,<2;python_version<'3.10'",
"numpy>=1.23.3,<2;python_version>='3.10'",
"pandas>=1.2.0,<2;python_version<'3.10'",
"pandas>=1.3.0,<2;python_version>='3.10'",
'humanfriendly>=8.2,<11',
'pandas @ git+https://github.com/pandas-dev/pandas.git@master#egg=pandas'
]

# Run
minimum_versions_39 = _get_minimum_versions(dependencies, '3.9')
minimum_versions_310 = _get_minimum_versions(dependencies, '3.10')

# Assert
expected_versions_39 = [
'numpy==1.20.0',
'pandas @ git+https://github.com/pandas-dev/pandas.git@master#egg=pandas',
'humanfriendly==8.2',
]
expected_versions_310 = [
'numpy==1.23.3',
'pandas @ git+https://github.com/pandas-dev/pandas.git@master#egg=pandas',
'humanfriendly==8.2',
]

assert minimum_versions_39 == expected_versions_39
assert minimum_versions_310 == expected_versions_310

0 comments on commit 0c42168

Please sign in to comment.