Skip to content

Commit

Permalink
Add Python 3.12 support (#550)
Browse files Browse the repository at this point in the history
  • Loading branch information
fealho authored Apr 6, 2024
1 parent 2b85305 commit d0f156f
Show file tree
Hide file tree
Showing 16 changed files with 75 additions and 60 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v1
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Install dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/minimum.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/readme.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
os: [ubuntu-latest, macos-latest] # skip windows bc rundoc fails
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand All @@ -26,4 +26,4 @@ jobs:
run: invoke unit
- if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.8
name: Upload codecov report
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v4
2 changes: 1 addition & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Requirements

**SDMetrics** has been developed and tested on [Python 3.8, 3.9, 3.10 and 3.11](https://www.python.org/downloads/)
**SDMetrics** has been developed and tested on [Python 3.8, 3.9, 3.10, 3.11 and 3.12](https://www.python.org/downloads/)

Also, although it is not strictly required, the usage of a [virtualenv](
https://virtualenv.pypa.io/en/latest/) is highly recommended in order to avoid
Expand Down
27 changes: 15 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,29 @@ classifiers = [
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
]
keywords = ['sdmetrics', 'sdmetrics', 'SDMetrics']
dynamic = ['version']
license = { text = 'MIT license' }
requires-python = ">=3.8,<3.12"
requires-python = ">=3.8,<3.13"
readme = 'README.md'
dependencies = [
"numpy>=1.20.0;python_version<'3.10'",
"numpy>=1.23.3;python_version>='3.10'",
"numpy>=1.23.3;python_version>='3.10' and python_version<'3.12'",
"numpy>=1.26.0;python_version>='3.12'",
"pandas>=1.1.3;python_version<'3.10'",
"pandas>=1.3.4;python_version>='3.10' and python_version<'3.11'",
"pandas>=1.5.0;python_version>='3.11'",
"scikit-learn>=0.24;python_version<'3.10'",
"scikit-learn>=1.1.3;python_version>='3.10'",
"scikit-learn>=1.1.3;python_version>='3.10' and python_version<'3.12'",
"scikit-learn>=1.3.0;python_version>='3.12'",
"scipy>=1.5.4;python_version<'3.10'",
"scipy>=1.9.2;python_version>='3.10'",
'copulas>=0.10.0',
'tqdm>=4.15',
'plotly>=5.10.0',
"scipy>=1.9.2;python_version>='3.10' and python_version<'3.12'",
"scipy>=1.12.0;python_version>='3.12'",
'copulas @ git+https://github.com/sdv-dev/Copulas@main',
'tqdm>=4.29',
'plotly>=5.19.0',
]

[project.urls]
Expand All @@ -46,15 +50,15 @@ sdmetrics = { main = 'sdmetrics.cli.__main__:main' }
[project.optional-dependencies]
torch = [
"torch>=1.8.0;python_version<'3.10'",
"torch>=1.11.0;python_version>='3.10' and python_version<'3.11'",
"torch>=2.0.0;python_version>='3.11'",
"torch>=2.0.0;python_version>='3.10' and python_version<'3.12'",
"torch>=2.2.0;python_version>='3.12'",
]
pomegranate = ['pomegranate>=0.14.1,<0.14.7']
test = [
'sdmetrics[torch]',
'pytest>=6.2.5,<7',
'pytest-cov>=2.6.0,<3',
'pytest-rerunfailures>=10',
'pytest-rerunfailures>=10.3,<15',
'jupyter>=1.0.0,<2',
'rundoc>=0.4.3,<0.5',
'tomli>=2.0.0,<3',
Expand All @@ -68,7 +72,7 @@ dev = [
'build>=1.0.0,<2',
'bump-my-version>=0.18.3,<1',
'pip>=9.0.1',
'watchdog>=0.8.3,<0.11',
'watchdog>=1.0.1,<5',

# style check
'flake8>=3.7.7,<4',
Expand Down Expand Up @@ -132,7 +136,6 @@ namespaces = false
'*.gif'
]
'sdmetrics' = ['demos/*/*.json', 'demos/*/*.csv']
'tests' = ['*']

[tool.setuptools.exclude-package-data]
'*' = [
Expand Down
10 changes: 8 additions & 2 deletions sdmetrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

import sys
import warnings as python_warnings
from importlib.metadata import entry_points
from operator import attrgetter
from types import ModuleType

import pandas as pd
from pkg_resources import iter_entry_points

from sdmetrics import (
column_pairs, demos, goal, multi_table, reports, single_column, single_table, timeseries)
Expand Down Expand Up @@ -134,7 +134,13 @@ def _find_addons():
from top_module.addon_module import x
"""
group = 'sdmetrics_modules'
for entry_point in iter_entry_points(group=group):
try:
eps = entry_points(group=group)
except TypeError:
# Load-time selection requires Python >= 3.10 or importlib_metadata >= 3.6
eps = entry_points().get(group, [])

for entry_point in eps:
try:
addon = entry_point.load()
except Exception: # pylint: disable=broad-exception-caught
Expand Down
6 changes: 3 additions & 3 deletions sdmetrics/reports/base_report.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Single table base report."""
import importlib.metadata
import pickle
import sys
import time
Expand All @@ -9,7 +10,6 @@

import numpy as np
import pandas as pd
import pkg_resources
import tqdm

from sdmetrics.reports.utils import convert_datetime_columns
Expand Down Expand Up @@ -287,7 +287,7 @@ def save(self, filepath):
filepath (str):
The path to the file where the report instance will be serialized.
"""
self._package_version = pkg_resources.get_distribution('sdmetrics').version
self._package_version = importlib.metadata.version('sdmetrics')

with open(filepath, 'wb') as output:
pickle.dump(self, output)
Expand All @@ -304,7 +304,7 @@ def load(cls, filepath):
SDMetrics Report:
The loaded report instance.
"""
current_version = pkg_resources.get_distribution('sdmetrics').version
current_version = importlib.metadata.version('sdmetrics')

with open(filepath, 'rb') as f:
report = pickle.load(f)
Expand Down
2 changes: 1 addition & 1 deletion sdmetrics/single_table/table_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def compute_breakdown(cls, real_data, synthetic_data):
real_columns = set(real_data.columns)
intersection_columns = real_columns & synthetic_columns
union_columns = real_columns | synthetic_columns
score = len(intersection_columns)/len(union_columns)
score = len(intersection_columns) / len(union_columns)

return {'score': score}

Expand Down
2 changes: 1 addition & 1 deletion tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def _get_minimum_versions(dependencies, python_version):
for dependency in dependencies:
if '@' in dependency:
name, url = dependency.split(' @ ')
min_versions[name] = f'{name} @ {url}'
min_versions[name] = f'{url}#egg={name}'
continue

req = Requirement(dependency)
Expand Down
18 changes: 12 additions & 6 deletions tests/unit/column_pairs/statistical/test_correlation_similarity.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ def test_compute_breakdown(self, pearson_mock):
result = metric.compute_breakdown(real_data, synthetic_data, coefficient='Pearson')

# Assert
assert pearson_mock.has_calls(
pearson_mock.assert_has_calls([
call(SeriesMatcher(real_data['col1']), SeriesMatcher(real_data['col2'])),
call(SeriesMatcher(synthetic_data['col1']), SeriesMatcher(synthetic_data['col2'])),
)
])
assert result == expected_score_breakdown

@patch('sdmetrics.column_pairs.statistical.correlation_similarity.pearsonr')
Expand Down Expand Up @@ -89,10 +89,16 @@ def test_compute_breakdown_datetime(self, pearson_mock):
result = metric.compute_breakdown(real_data, synthetic_data, coefficient='Pearson')

# Assert
assert pearson_mock.has_calls(
call(SeriesMatcher(real_data['col1']), SeriesMatcher(real_data['col2'])),
call(SeriesMatcher(synthetic_data['col1']), SeriesMatcher(synthetic_data['col2'])),
)
pearson_mock.assert_has_calls([
call(
SeriesMatcher(
real_data['col1'].astype('int64')), SeriesMatcher(
real_data['col2'].astype('int64'))),
call(
SeriesMatcher(
synthetic_data['col1'].astype('int64')), SeriesMatcher(
synthetic_data['col2'].astype('int64'))),
])
assert result == expected_score_breakdown

def test_compute_breakdown_constant_input(self):
Expand Down
10 changes: 5 additions & 5 deletions tests/unit/reports/test_base_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ def test_get_details(self):
base_report._properties['Property 1'].details.copy.assert_called_once()
base_report._properties['Property 2'].details.copy.assert_called_once()

@patch('sdmetrics.reports.base_report.pkg_resources.get_distribution')
@patch('sdmetrics.reports.base_report.importlib.metadata.version')
@patch('sdmetrics.reports.base_report.pickle')
def test_save(self, pickle_mock, get_distribution_mock):
"""Test the ``save`` method.
Expand All @@ -694,9 +694,9 @@ def test_save(self, pickle_mock, get_distribution_mock):
get_distribution_mock.assert_called_once_with('sdmetrics')
open_mock.assert_called_once_with('test-file.pkl', 'wb')
pickle_mock.dump.assert_called_once_with(report, open_mock())
assert report._package_version == get_distribution_mock.return_value.version
assert report._package_version == get_distribution_mock.return_value

@patch('sdmetrics.reports.base_report.pkg_resources.get_distribution')
@patch('sdmetrics.reports.base_report.importlib.metadata.version')
@patch('sdmetrics.reports.base_report.pickle')
def test_load(self, pickle_mock, get_distribution_mock):
"""Test the ``load`` method.
Expand Down Expand Up @@ -727,7 +727,7 @@ def test_load(self, pickle_mock, get_distribution_mock):
assert loaded == pickle_mock.load.return_value

@patch('sdmetrics.reports.base_report.warnings')
@patch('sdmetrics.reports.base_report.pkg_resources.get_distribution')
@patch('sdmetrics.reports.base_report.importlib.metadata.version')
@patch('sdmetrics.reports.base_report.pickle')
def test_load_mismatched_versions(self, pickle_mock, get_distribution_mock, warnings_mock):
"""Test the ``load`` method with mismatched sdmetrics versions.
Expand All @@ -748,7 +748,7 @@ def test_load_mismatched_versions(self, pickle_mock, get_distribution_mock, warn
report = Mock()
pickle_mock.load.return_value = report
report._package_version = 'previous_version'
get_distribution_mock.return_value.version = 'new_version'
get_distribution_mock.return_value = 'new_version'

# Run
with patch('sdmetrics.reports.base_report.open', open_mock):
Expand Down
14 changes: 7 additions & 7 deletions tests/unit/test___init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def mock_sdmetrics():
sys.modules['sdmetrics'] = sdmetrics_module


@patch.object(sdmetrics, 'iter_entry_points')
@patch.object(sdmetrics, 'entry_points')
def test__find_addons_module(entry_points_mock, mock_sdmetrics):
"""Test loading an add-on."""
# Setup
Expand All @@ -37,7 +37,7 @@ def test__find_addons_module(entry_points_mock, mock_sdmetrics):
assert sys.modules['sdmetrics.submodule.entry_name'] == add_on_mock


@patch.object(sdmetrics, 'iter_entry_points')
@patch.object(sdmetrics, 'entry_points')
def test__find_addons_object(entry_points_mock, mock_sdmetrics):
"""Test loading an add-on."""
# Setup
Expand All @@ -55,7 +55,7 @@ def test__find_addons_object(entry_points_mock, mock_sdmetrics):


@patch('warnings.warn')
@patch('sdmetrics.iter_entry_points')
@patch('sdmetrics.entry_points')
def test__find_addons_bad_addon(entry_points_mock, warning_mock):
"""Test failing to load an add-on generates a warning."""
# Setup
Expand All @@ -78,7 +78,7 @@ def entry_point_error():


@patch('warnings.warn')
@patch('sdmetrics.iter_entry_points')
@patch('sdmetrics.entry_points')
def test__find_addons_wrong_base(entry_points_mock, warning_mock):
"""Test incorrect add-on name generates a warning."""
# Setup
Expand All @@ -99,7 +99,7 @@ def test__find_addons_wrong_base(entry_points_mock, warning_mock):


@patch('warnings.warn')
@patch('sdmetrics.iter_entry_points')
@patch('sdmetrics.entry_points')
def test__find_addons_missing_submodule(entry_points_mock, warning_mock):
"""Test incorrect add-on name generates a warning."""
# Setup
Expand All @@ -120,7 +120,7 @@ def test__find_addons_missing_submodule(entry_points_mock, warning_mock):


@patch('warnings.warn')
@patch('sdmetrics.iter_entry_points')
@patch('sdmetrics.entry_points')
def test__find_addons_module_and_object(entry_points_mock, warning_mock):
"""Test incorrect add-on name generates a warning."""
# Setup
Expand All @@ -141,7 +141,7 @@ def test__find_addons_module_and_object(entry_points_mock, warning_mock):


@patch('warnings.warn')
@patch.object(sdmetrics, 'iter_entry_points')
@patch.object(sdmetrics, 'entry_points')
def test__find_addons_missing_object(entry_points_mock, warning_mock, mock_sdmetrics):
"""Test incorrect add-on name generates a warning."""
# Setup
Expand Down
Loading

0 comments on commit d0f156f

Please sign in to comment.